antd#PageHeader TypeScript Examples

The following examples show how to use antd#PageHeader. 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: DocumentEditor.tsx    From yakit with GNU Affero General Public License v3.0 6 votes vote down vote up
DocumentEditor: React.FC<DocumentEditorProp> = (props) => {
    const [markdown, setMarkdown] = useState(props.markdown);

    return <div>
        <PageHeader
            title={"编辑/添加模块文档"} subTitle={props.yakScript.ScriptName + `[${props.yakScript.Id}]`}
            extra={[
                <Button
                    type={"primary"}
                    onClick={e => {
                        ipcRenderer.invoke("SaveMarkdownDocument", {
                            YakScriptId: props.yakScript.Id,
                            YakScriptName: props.yakScript.ScriptName,
                            Markdown: markdown,
                        }).then(() => {
                            success("保存文档成功")
                        }).catch((e: any) => {
                            console.info(e)
                        }).finally()
                    }}
                >保存 / 创建文档</Button>
            ]}
        />
        <MDEditor
            value={markdown}
            onChange={e => setMarkdown(e || "")}
            maxHeight={1000} height={700}
        >

        </MDEditor>
    </div>
}
Example #2
Source File: PageContent.tsx    From iot-center-v2 with MIT License 6 votes vote down vote up
PageContent: FunctionComponent<PageContentProps> = (props) => (
  <Layout.Content
    style={{
      paddingLeft: 60,
      paddingRight: 60,
      paddingTop: 55,
      margin: 0,
      minHeight: 280,
      minWidth: 350,
      height: '100vh',
      overflowY: props.forceShowScroll ? 'scroll' : 'auto',
    }}
  >
    <PageHeader
      title={props.title}
      style={{paddingLeft: 0, paddingRight: 0, paddingTop: 0}}
      extra={props?.titleExtra}
    />
    {props.message ? (
      <Alert
        message={props.message.title}
        description={props.message.description}
        type={props.message.type}
        showIcon
        closable
      />
    ) : undefined}
    <div className="site-layout-background" style={{minHeight: 360}}>
      <Spin spinning={props.spin ?? false}>{props.children}</Spin>
    </div>
  </Layout.Content>
)
Example #3
Source File: AnalyzerPage.tsx    From yakit with GNU Affero General Public License v3.0 6 votes vote down vote up
AnalyzerPage: React.FC<AnalyzerPageProp> = (props) => {
    const [response, setResponse] = useState<any>();
    const [error, setError] = useState("");

    useEffect(() => {
        ipcRenderer.invoke("http-analyze", {
            IsHTTPS: props.isHttps,
            Request: props.request,
            Response: props.response,
        })
    }, [props])

    useEffect(() => {
        ipcRenderer.on("client-http-analyze-data", (e: any, data: any) => {
        })
        ipcRenderer.on("client-http-analyze-error", (e: any, details: any) => {
            setError(details)
        })
        return () => {
        }
    }, [])

    return <div>
        <PageHeader title={"HTTP 模糊测试分析器"}/>
        <Row gutter={8}>
            <Col span={12}>
                <div style={{height: 500}}>
                    <YakEditor value={props.request} readOnly={true}/>
                </div>
            </Col>
            <Col span={12}>
                <div style={{height: 500}}>
                    <YakEditor value={props.response} readOnly={true}/>
                </div>
            </Col>
        </Row>
    </div>
}
Example #4
Source File: index.tsx    From nebula-studio with Apache License 2.0 6 votes vote down vote up
NebulaBreadcrumb: React.FC<IProps> = (props: IProps) => {
  const { routes, extraNode } = props;
  return (
    <PageHeader
      title={null}
      className={styles.studioBreadcrumb}
      breadcrumbRender={() => {
        return <div className={cls(styles.breadcrumbContainer, 'studioCenterLayout')}>
          <Breadcrumb
            className={styles.breadcrumb}
            routes={routes} 
            itemRender={itemRender} 
          />
          {extraNode}
        </div>;
      }}
    />
  );
}
Example #5
Source File: Page.tsx    From wildduck-ui with MIT License 6 votes vote down vote up
/** Page Renderer */
	render(): JSX.Element {
		const { children, error, loading, title, subTitle, extra } = this.props;
		return (
			<PageHeader title={title} subTitle={subTitle} extra={extra} className={this.props.className}>
				{React.Children.map(children, (childElement) => {
					return React.cloneElement(childElement as React.ReactElement<any>, {
						error,
						loading,
					});
				})}
			</PageHeader>
		);
	}
Example #6
Source File: index.tsx    From surveyo with Apache License 2.0 6 votes vote down vote up
function Graphiql() {
  const GRAPHQL_ENDPOINT = process.env.REACT_APP_GRAPHQL_ENDPOINT;
  const {id} = useParams();
  const {getIdTokenClaims} = useAuth0();
  const graphQLFetcher = async (graphQLParams: any) => {
    const token = await getIdTokenClaims();
    return await fetch(GRAPHQL_ENDPOINT!, {
      method: 'post',
      headers: {
        'Content-Type': 'application/json',
        'X-Auth-Token': token.__raw,
      },
      body: JSON.stringify(graphQLParams),
    }).then(response => response.json());
  };

  return (
    <PageHeader ghost title="Query">
      <Layout style={{height: '80vh'}}>
        <Row style={{height: '100%'}}>
          <GraphiQL fetcher={graphQLFetcher} query={makeDefaultQuery(id)} />
        </Row>
      </Layout>
    </PageHeader>
  );
}
Example #7
Source File: index.tsx    From surveyo with Apache License 2.0 6 votes vote down vote up
function PageLoading() {
  return (
    <PageHeader title="...">
      <Row gutter={[16, 16]}>
        <Col span={24}>
          <Card loading />
        </Col>
      </Row>

      <Row gutter={[16, 16]}>
        <Col span={24}>
          <Card loading />
        </Col>
      </Row>

      <Row gutter={[16, 16]}>
        <Col span={24}>
          <Card loading />
        </Col>
      </Row>
    </PageHeader>
  );
}
Example #8
Source File: index.tsx    From surveyo with Apache License 2.0 6 votes vote down vote up
export default function Dashboard() {
  return (
    <PageHeader
      ghost={true}
      title="Dashboard"
      extra={[
        <Link to="/create">
          <Button icon={<PlusOutlined />} type="primary">
            New survey
          </Button>
        </Link>,
      ]}
    >
      <DashboardHelper />
    </PageHeader>
  );
}
Example #9
Source File: CommonApp.tsx    From disco-cube-admin with MIT License 6 votes vote down vote up
CommonApp: React.FC<Props> = ({
  onBack,
  isRunning,
  isCommand,
  onStart,
  onStop,
  appName,
}) => {
  return (
    <Segment spacing={10} width="100%" maxWidth={500} height={"100%"}>
      <PageHeader
        ghost={false}
        onBack={onBack}
        title="Apps"
        subTitle={appName.toUpperCase()}
      ></PageHeader>
      <VerticalSpacer space={10} />
      {!isRunning && (
        <Button type="primary" disabled={isCommand} onClick={() => onStart({})}>
          Start
        </Button>
      )}
      {isRunning && (
        <Button type="primary" onClick={onStop}>
          Stop
        </Button>
      )}
    </Segment>
  );
}
Example #10
Source File: CubemapApp.tsx    From disco-cube-admin with MIT License 6 votes vote down vote up
CubemapApp: React.FC<Props> = ({
  onBack,
  onStartCubemap,
  isRunning,
  onStopCubemap,
  isCommand,
}) => {
  const [selected, setSelected] = React.useState(randomOne(cubemaps));

  return (
    <Segment spacing={10} width="100%" maxWidth={500} height={"100%"}>
      <PageHeader ghost={false} onBack={onBack} title="Apps" subTitle="Cubemap"></PageHeader>
      <VerticalSpacer space={10} />
      <Vertical spacing={20}>
        <Select defaultValue={selected} value={selected} onChange={value => setSelected(value)}>
          {Object.values(cubemaps).map(id => (
            <Select.Option key={id} value={id}>
              {id}
            </Select.Option>
          ))}
        </Select>
        {!isRunning && (
          <Button
            type="primary"
            disabled={isCommand}
            onClick={() => onStartCubemap({ cubemapId: selected })}
          >
            Start
          </Button>
        )}
        {isRunning && (
          <Button type="primary" onClick={onStopCubemap}>
            Stop
          </Button>
        )}
      </Vertical>
    </Segment>
  );
}
Example #11
Source File: PluginExecutor.tsx    From yakit with GNU Affero General Public License v3.0 5 votes vote down vote up
PluginExecutor: React.FC<PluginExecutorProp> = (props) => {
    const {script, settingShow, settingNode} = props;

    const [token, setToken] = useState(randomString(40));
    const [loading, setLoading] = useState(false);

    const [infoState, {reset, setXtermRef}, xtermRef] = useHoldingIPCRStream(
        script.ScriptName,
        "exec-yak-script",
        token,
        () => {
            setTimeout(() => setLoading(false), 300)
        }
    )

    const executeByParams = useMemoizedFn((p: YakExecutorParam[]) => {
        setLoading(true)

        setTimeout(() => {
            ipcRenderer.invoke("exec-yak-script", {
                Params: [...p, ...(props.extraYakExecutorParams || [])],
                YakScriptId: props.script.Id,
            }, token)
        }, 300);
    })

    return <div style={{height: "100%", display: "flex", flexFlow: "column"}}>
        <PageHeader
            title={script.ScriptName} style={{marginBottom: 0, paddingBottom: 0}}
            subTitle={props.subTitle}
            extra={props.extraNode}
        >
            {!!settingShow && settingNode}
            <YakScriptParamsSetter
                {...script}
                loading={loading}
                onParamsConfirm={executeByParams}
                onClearData={() => {
                    xtermClear(xtermRef)
                    reset()
                }}
                onCanceled={() => {
                    ipcRenderer.invoke("cancel-exec-yak-script", token)
                }}
                styleSize={props.size}
                submitVerbose={"开始执行"}
                primaryParamsOnly={true}
            />
        </PageHeader>
        <Divider/>
        <PluginResultUI
            script={script} loading={loading} progress={infoState.processState} results={infoState.messageState}
            featureType={infoState.featureTypeState}
            feature={infoState.featureMessageState}
            statusCards={infoState.statusState} onXtermRef={setXtermRef}
        />
    </div>
}
Example #12
Source File: index.tsx    From nebula-dashboard with Apache License 2.0 5 votes vote down vote up
render() {
    const { breadcrumb: routes, title, showBackBtn, extra } = this.props.config;
    const itemRender = (route, _params, routes, _paths) => {
      const last = routes.indexOf(route) === routes.length - 1;
      return last ? (
        <span>{route.breadcrumbName}</span>
      ) : (
        <Link to={route.path}>{route.breadcrumbName}</Link>
      );
    };
    const { pathname } = this.props.location;
    const currentPage = extra
      ? extra.filter(item => item.value === pathname)
      : null;
    return (
      <PageHeader
        className="page-header"
        title={
          <>
            {showBackBtn && (
              <Icon
                className="btn-return blue"
                icon="#iconreturn"
                onClick={this.handleBack}
              />
            )}
            <span>{title}</span>
          </>
        }
        breadcrumb={{ itemRender, routes }}
        extra={
          extra && currentPage.length !== 0 ? (
            <Radio.Group
              options={extra}
              onChange={this.handlePageView}
              value={currentPage[0].value}
              optionType="button"
              buttonStyle="solid"
            />
          ) : null
        }
      />
    );
  }
Example #13
Source File: index.tsx    From surveyo with Apache License 2.0 5 votes vote down vote up
function GqlViz() {
  const {id} = useParams();

  const {loading, error, data} = useQuery<GetChartData, GetChartDataVariables>(
    GET_CHART_DATA,
    {
      variables: {id},
    }
  );

  if (loading) {
    return <Card title loading />;
  }

  if (error) {
    return (
      <Card title>
        <Alert message={error.message} type="warning" />
      </Card>
    );
  }

  const makeChart = (field: GetChartData_getForm_fields) => {
    switch (field.type) {
      case 'NetPromoterScore':
        return <ChartNetPromoterScore {...field} />;
      case 'Rating':
        return <ChartRating {...field} />;
      case 'SingleChoice':
        return <ChartSingleChoice {...field} />;
      case 'Text':
        return <ChartText {...field} />;
      default:
        return null;
    }
  };
  
  return (
    <PageHeader ghost={true} title={data!.getForm!.title}>
      <Row gutter={[16, 16]}>
        {data!.getForm!.fields.map(field => {
          const chart = makeChart(field);
          if (chart) {
            return (
              <Col span={12}>
                <Card style={{height: '100%'}}>
                  <h3>{field.title}</h3>
                  <div style={{height: 'fit-content'}}>{chart}</div>
                </Card>
              </Col>
            );
          } else {
            return null;
          }
        })}
      </Row>
    </PageHeader>
  );
}
Example #14
Source File: VideoApp.tsx    From disco-cube-admin with MIT License 5 votes vote down vote up
VideoApp: React.FC<Props> = ({
  onBack,
  onStartVideo,
  isRunning,
  onStopVideo,
  isCommand,
}) => {
  const [selectedVideo, setSelectedVideo] = React.useState(randomOne(videos));

  return (
    <Segment spacing={10} width="100%" maxWidth={500} height={"100%"}>
      <PageHeader ghost={false} onBack={onBack} title="Apps" subTitle="Video"></PageHeader>
      <VerticalSpacer space={10} />
      <Vertical spacing={20}>
        <Select
          defaultValue={selectedVideo}
          value={selectedVideo}
          onChange={value => setSelectedVideo(value)}
        >
          {Object.values(videos).map(id => (
            <Select.Option key={id} value={id}>
              {id}
            </Select.Option>
          ))}
        </Select>
        {!isRunning && (
          <Button
            type="primary"
            disabled={isCommand}
            onClick={() => onStartVideo({ videoId: selectedVideo })}
          >
            Start
          </Button>
        )}
        {isRunning && (
          <Button type="primary" onClick={onStopVideo}>
            Stop
          </Button>
        )}
      </Vertical>
    </Segment>
  );
}
Example #15
Source File: RPIDemos.tsx    From disco-cube-admin with MIT License 5 votes vote down vote up
RPIDemos: React.FC<Props> = ({
  onBack,
  onStartDemo,
  isRunning,
  onStopDemo,
  isCommand,
}) => {
  const [selectedDemoId, setSelectedDemoId] = React.useState(`D0`);

  return (
    <Segment spacing={10} width="100%" maxWidth={500} height={"100%"}>
      <PageHeader ghost={false} onBack={onBack} title="Apps" subTitle="RPI Demos"></PageHeader>
      <VerticalSpacer space={10} />
      <Vertical spacing={20}>
        <Select
          defaultValue={selectedDemoId}
          value={selectedDemoId}
          onChange={value => setSelectedDemoId(value)}
        >
          {Object.keys(demos).map(id => (
            <Select.Option key={id} value={id} disabled={(demos as any)[id].disabled}>
              {id} - {(demos as any)[id].description}
            </Select.Option>
          ))}
        </Select>
        {!isRunning && (
          <Button
            type="primary"
            disabled={isCommand}
            onClick={() => onStartDemo({ demoId: selectedDemoId })}
          >
            Start
          </Button>
        )}
        {isRunning && (
          <Button type="primary" onClick={onStopDemo}>
            Stop
          </Button>
        )}
      </Vertical>
    </Segment>
  );
}
Example #16
Source File: PaintApp.tsx    From disco-cube-admin with MIT License 5 votes vote down vote up
PaintApp: React.FC<Props> = ({
  onBack,
  isRunning,
  isCommand,
  onStart,
  onAppStateUpdated,
  onStop,
}) => {
  const [settings, setSettings] = React.useState<PaintingSettings>({
    bushSize: 1,
    brushColor: { r: 255, g: 0, b: 0 },
  });

  return (
    <Segment spacing={10} width="100%" maxWidth={500} height={"100%"}>
      <PageHeader
        ghost={false}
        onBack={onBack}
        title="Apps"
        subTitle={"PAINT"}
        extra={[
          isRunning ? (
            <Button key="stop" type="primary" onClick={onStop}>
              Stop
            </Button>
          ) : (
            <Button key="start" type="primary" disabled={isCommand} onClick={() => onStart({})}>
              start
            </Button>
          ),
        ]}
      ></PageHeader>
      <VerticalSpacer space={10} />
      {isRunning && (
        <Vertical spacing={10}>
          <PaintControls settings={settings} onSettingsChange={setSettings} />
          <Tabs
            tabBarStyle={{ textAlign: "center", marginTop: 0 }}
            size="large"
            tabPosition="bottom"
            type="card"
          >
            {narray(6).map(i => (
              <Tabs.TabPane tab={i + ""} key={i + ""}>
                <PaintCanvas
                  width={64}
                  height={64}
                  settings={settings}
                  onDataChanged={data => onAppStateUpdated({ face: i, data })}
                />
              </Tabs.TabPane>
            ))}
          </Tabs>
        </Vertical>
      )}
    </Segment>
  );
}
Example #17
Source File: index.tsx    From S2 with MIT License 5 votes vote down vote up
Header: React.FC<HeaderProps> = React.memo(
  ({
    className,
    title,
    width,
    description,
    exportCfg,
    advancedSortCfg,
    switcherCfg,
    sheet,
    extra,
    dataCfg,
    options,
    ...restProps
  }) => {
    const PRE_CLASS = 's2-header';

    const getExtraComponents = () => {
      return (
        <>
          {extra}
          {switcherCfg.open && (
            <SwitcherHeader
              sheet={sheet}
              dataCfg={dataCfg}
              options={options}
              {...switcherCfg}
            />
          )}
          {advancedSortCfg.open && (
            <AdvancedSort sheet={sheet} {...advancedSortCfg} />
          )}
          {exportCfg.open && (
            <Export key={'export'} sheet={sheet} {...exportCfg} />
          )}
        </>
      );
    };

    return (
      <PageHeader
        className={cx(PRE_CLASS, className)}
        style={{ width }}
        ghost={false}
        title={title}
        extra={getExtraComponents()}
        {...restProps}
      >
        {description}
      </PageHeader>
    );
  },
)
Example #18
Source File: index.tsx    From yugong with MIT License 5 votes vote down vote up
Index: React.FC<CustomPersetProps> = ({ runningData, onChange }) => {
  const btnreset = get(runningData, '[0].arguments[1].data');
  const btnsubmit = get(runningData, '[0].arguments[2].data');

  const handleChangeReset = useCallback(
    (e) => {
      const runD = cloneDeep(runningData);
      set(runD, '[0].arguments[1].data', e.target.value)
      onChange(runD)
    },
    [onChange, runningData],
  )
  
  const handleChangeSubmit = useCallback(
    (e) => {
      const runD = cloneDeep(runningData);
      set(runD, '[0].arguments[2].data', e.target.value)
      onChange(runD)
    },
    [onChange, runningData],
  )

  return (
    <FormModuleContext.Provider
      value={{
        runningData,
        onChangeRunningData: onChange,
        dataPath: '[0].arguments[0].data'
      }}
    >
      <PageHeader title="表单项" />
      <SortableFormData />
      <PageHeader title="表单按钮" />
      <LineItem label="重置">
        <Input value={btnreset} placeholder="重置" onChange={handleChangeReset}/>
      </LineItem>
      <LineItem label="提交">
        <Input value={btnsubmit} placeholder="提交" onChange={handleChangeSubmit} />
      </LineItem>
    </FormModuleContext.Provider>
  );
}
Example #19
Source File: AppsPageContent.tsx    From disco-cube-admin with MIT License 5 votes vote down vote up
AppsPageContent: React.FC<Props> = ({
  onOpenPage,
  runningAppName,
  onCancelCommand,
  command,
  onStopApp,
}) => {
  return (
    <Segment spacing={10} width="100%" maxWidth={500} height={"100%"}>
      <PageHeader
        ghost={false}
        //onBack={() => window.history.back()}
        title="Apps"
        //subTitle="This is a subtitle"
      ></PageHeader>
      <Vertical spacing={5}>
        <Label>RUNNING APP</Label>
        <div>{runningAppName ? runningAppName : "NONE"}</div>
        {runningAppName && (
          <Button onClick={onStopApp} type="primary" style={{ width: 200 }}>
            Stop
          </Button>
        )}
      </Vertical>

      <VerticalSpacer space={20} />

      <Vertical spacing={5}>
        <Label>command</Label>
        <div>{command ? command : "NONE"}</div>
        {command && (
          <Button onClick={onCancelCommand} type="primary" style={{ width: 200 }}>
            Cancel
          </Button>
        )}
      </Vertical>

      <VerticalSpacer space={20} />
      <Label>APPs</Label>
      <Grid spacing={20}>
        {Object.entries(apps).map(([key, { path, icon: Icon, label }]) => (
          <Button
            key={key}
            type="primary"
            style={{ width: 100, height: 100 }}
            onClick={() => onOpenPage(path)}
          >
            <Vertical horizontalAlign="center" verticalAlign="center" spacing={8}>
              <Icon style={iconStyles} />
              <div>{label}</div>
            </Vertical>
          </Button>
        ))}
      </Grid>
    </Segment>
  );
}
Example #20
Source File: HTTPFlowDetail.tsx    From yakit with GNU Affero General Public License v3.0 4 votes vote down vote up
HTTPFlowDetail: React.FC<HTTPFlowDetailProp> = (props) => {
    const [flow, setFlow] = useState<HTTPFlow>();
    const [loading, setLoading] = useState(false);

    const actionFuzzer = [
        {
            id: 'send-fuzzer-info',
            label: '发送到Fuzzer',
            contextMenuGroupId: 'send-fuzzer-info',
            run: () => {
                ipcRenderer.invoke("send-to-tab", {
                    type: "fuzzer",
                    data: {
                        isHttps: flow?.IsHTTPS,
                        request: Buffer.from(flow?.Request || []).toString("utf8")
                    }
                })
                if (props.onClose) props.onClose()
            }
        },
        {
            id: 'send-to-plugin',
            label: '发送到数据包扫描',
            contextMenuGroupId: 'send-fuzzer-info',
            run: () => ipcRenderer.invoke("send-to-packet-hack", {
                request: flow?.Request,
                ishttps: flow?.IsHTTPS,
                response: flow?.Response
            })

        }
    ]

    useEffect(() => {
        if (!props.hash) {
            return
        }
        //
        // ipcRenderer.on(props.hash, (e: any, data: HTTPFlow) => {
        //     setFlow(data)
        //     setTimeout(() => setLoading(false), 300)
        // })
        // ipcRenderer.on(`ERROR:${props.hash}`, (e: any, details: any) => {
        //     failed(`查询该请求失败[${props.hash}]: ` + details)
        // })

        setLoading(true)
        ipcRenderer.invoke("GetHTTPFlowByHash", {Hash: props.hash}).then((data: HTTPFlow) => {
            setFlow(data)
        }).catch(e => {
            failed(`GetHTTPFlow ByHash[${props.hash}] failed`)
        }).finally(() => setTimeout(() => setLoading(false), 300))
        // ipcRenderer.invoke("get-http-flow", props.hash)

        return () => {
            // ipcRenderer.removeAllListeners(props.hash)
            // ipcRenderer.removeAllListeners(`ERROR:${props.hash}`)
        }
    }, [props.hash])

    return <Spin spinning={loading} style={{width: "100%", marginBottom: 24}}>
        {flow ? <>
            {props.noHeader ? undefined : <PageHeader
                title={`请求详情`} subTitle={props.hash}
                extra={
                    props.fetchRequest ?
                        <Space>
                            <Tooltip title={"上一个请求"}>
                                <Button type="link" disabled={!!props.isFront}
                                        icon={<LeftOutlined/>} onClick={() => {
                                    props?.fetchRequest!(1)
                                }}></Button>
                            </Tooltip>
                            <Tooltip title={"下一个请求"}>
                                <Button type="link" disabled={!!props.isBehind}
                                        icon={<RightOutlined/>} onClick={() => {
                                    props?.fetchRequest!(2)
                                }}></Button>
                            </Tooltip>
                        </Space>
                        :
                        <></>
                }/>
            }
            <Space direction={"vertical"} style={{width: "100%"}}>
                <Descriptions column={4} bordered={true} size={"small"}>
                    <Descriptions.Item key={"method"} span={1} label={"HTTP 方法"}><Tag color={"geekblue"}><Text
                        style={{maxWidth: 500}}>{flow.Method}</Text></Tag></Descriptions.Item>
                    <Descriptions.Item key={"url"} span={3} label={"请求 URL"}>
                        <Text style={{maxWidth: 500}} copyable={true}>{flow.Url}</Text>
                    </Descriptions.Item>
                    <Descriptions.Item key={"https"} span={1} label={"HTTPS"}><Tag color={"geekblue"}>
                        <div
                            style={{maxWidth: 500}}>{flow.IsHTTPS ? "True" : "False"}</div>
                    </Tag></Descriptions.Item>
                    <Descriptions.Item key={"status"} span={1} label={"StatusCode"}><Tag
                        color={"geekblue"}>{flow.StatusCode}</Tag></Descriptions.Item>
                    <Descriptions.Item key={"size"} span={1} label={"Body大小"}><Tag color={"geekblue"}>
                        <div style={{maxWidth: 500}}>{flow.BodySizeVerbose}</div>
                    </Tag></Descriptions.Item>
                    <Descriptions.Item key={"type"} span={1} label={"Content-Type"}><Tag color={"geekblue"}>
                        <div style={{maxWidth: 500}}>{flow.ContentType}</div>
                    </Tag></Descriptions.Item>
                </Descriptions>
                <div style={{width: "100%", overflow: "auto"}}>
                    {flow.GetParams.length > 0 || flow.PostParams.length > 0 || flow.CookieParams.length > 0 ? <Tabs>
                        {flow.GetParams.length > 0 && <Tabs.TabPane key={"get"} tab={"GET 参数"}>
                            <FuzzableParamList data={flow.GetParams} sendToWebFuzzer={() => {
                                if (props.onClose) props.onClose()
                            }}/>
                        </Tabs.TabPane>}
                        {flow.PostParams.length > 0 && <Tabs.TabPane key={"post"} tab={"POST 参数"}>
                            <FuzzableParamList data={flow.PostParams} sendToWebFuzzer={() => {
                                if (props.onClose) props.onClose()
                            }}/>
                        </Tabs.TabPane>}
                        {flow.CookieParams.length > 0 && <Tabs.TabPane key={"cookie"} tab={"Cookie 参数"}>
                            <FuzzableParamList data={flow.CookieParams} sendToWebFuzzer={() => {
                                if (props.onClose) props.onClose()
                            }}/>
                        </Tabs.TabPane>}
                    </Tabs> : ""}
                </div>

                <Row gutter={8}>
                    <Col span={12}>
                        <Card title={"原始 HTTP 请求"} size={"small"} bodyStyle={{padding: 0}}>
                            <div style={{height: 350}}>
                                <YakEditor readOnly={true} type={"http"}//theme={"fuzz-http-theme"}
                                           value={new Buffer(flow.Request).toString("utf-8")}
                                           actions={[...actionFuzzer]}/>
                            </div>
                        </Card>
                    </Col>
                    <Col span={12}>
                        <Card title={"原始 HTTP 响应"} size={"small"} bodyStyle={{padding: 0}}>
                            <div style={{height: 350}}>
                                <YakEditor readOnly={true} type={"http"}// theme={"fuzz-http-theme"}
                                           value={new Buffer(flow.Response).toString("utf-8")}
                                />
                            </div>
                        </Card>
                    </Col>
                </Row>

                {/*<Collapse>*/}
                {/*    <Collapse.Panel key={"request-raw"} header={"原始 HTTP 请求数据包内容"}>*/}

                {/*    </Collapse.Panel>*/}
                {/*    <Collapse.Panel key={"response-raw"} header={"原始 HTTP 响应数据包内容"}>*/}

                {/*    </Collapse.Panel>*/}
                {/*</Collapse>*/}
                <Row gutter={8}>
                    <Col span={12}>
                        <Collapse defaultActiveKey={"request"}>
                            <Collapse.Panel key={"request"} header={"Request Headers"}>
                                <Descriptions bordered={true} column={1} size={"small"}>
                                    {(flow?.RequestHeader || []).sort((i, e) => {
                                        return i.Header.localeCompare(e.Header)
                                    }).map(i => {
                                        return <Descriptions.Item key={i.Header} label={<Text style={{width: 240}}>
                                            <Tag>{i.Header}</Tag>
                                        </Text>}>
                                            <Text
                                                copyable={true}
                                                style={{maxWidth: 500}}
                                                ellipsis={{tooltip: true}}>{i.Value}</Text>
                                        </Descriptions.Item>
                                    })}
                                </Descriptions>
                            </Collapse.Panel>
                        </Collapse>
                    </Col>
                    <Col span={12}>
                        <Collapse defaultActiveKey={"response"}>
                            <Collapse.Panel key={"response"} header={"Response Headers"}>
                                <Descriptions bordered={true} column={1} size={"small"}>
                                    {(flow?.ResponseHeader || []).sort((i, e) => {
                                        return i.Header.localeCompare(e.Header)
                                    }).map(i => {
                                        return <Descriptions.Item key={i.Header} label={<Text style={{width: 240}}>
                                            <Tag>{i.Header}</Tag>
                                        </Text>}>
                                            <Text
                                                copyable={true}
                                                style={{maxWidth: 500}}
                                                ellipsis={{tooltip: true}}>{i.Value}</Text>
                                        </Descriptions.Item>
                                    })}
                                </Descriptions>
                            </Collapse.Panel>
                        </Collapse>
                    </Col>
                </Row>
            </Space>
        </> : ""}
    </Spin>
}
Example #21
Source File: index.tsx    From dnde with GNU General Public License v3.0 4 votes vote down vote up
EditPage = () => {
  const ref = useRef<any>(null);
  const { templateId }: { templateId: string | undefined } = useParams();
  const [trigger, { data, isError, isLoading, isSuccess }] = useLazyGetTemplateQuery();

  useEffect(() => {
    if (templateId === 'new' || typeof templateId === 'undefined') {
      ref.current && ref.current.loadJson(null);
    } else {
      if (templateId) {
        message.loading({ content: 'Fetching Template...', key: LOADING_KEY, duration: 0 });
        trigger({ id: templateId });
      }
    }
  }, []);

  useEffect(() => {
    if (isSuccess && data) {
      try {
        ref.current && ref.current.loadJson(data.response.data);
      } catch (e) {
        message.error('Unable to load template', 3);
      }
    } else if (isSuccess && !data) {
      message.error('Template is empty', 2);
    }
    if (isSuccess) {
      message.destroy(LOADING_KEY);
    }
    if (isError) {
      message.info('Network error, template not fetched.', 2);
    }
  }, [isError, isLoading, isSuccess, data]);

  const copyJsonInClipBoard = (e: any) => {
    if (ref.current) {
      e.preventDefault();
      const json = ref.current.getJson();
      logger.log('json', json);
      navigator.clipboard.writeText(json);
      success('Copied to Clipboard & logged in devtools ');
    }
  };

  const copyHTMLAsClipBoard = (e: any) => {
    if (ref.current) {
      const html = ref.current.getHtml();
      navigator.clipboard.writeText(html);
      logger.log('html', html);
      success('Copied to clipboard & logged in devtools ');
      e.preventDefault();
    }
  };

  const copyPreviewImage = async (e: any) => {
    if (ref.current) {
      e.preventDefault();
      const html = ref.current.html;
      navigator.clipboard.writeText(await generatePreview(html));
      success('Preview Image Copied to clipboard');
    }
  };

  return (
    <div style={{ flex: '1', display: 'flex', width: '100%', height: '100%' }}>
      <Row style={{ height: '100%', width: '100%' }} justify="center">
        <Prompt
          when={UNDOREDO.undo.length > 1 || UNDOREDO.redo.length > 1}
          message={() => 'Are you sure you want to leave, your changes will be lost'}
        />
        <Col lg={24} xl={0}>
          <div style={{ textAlign: 'center', padding: '40px', paddingTop: '10%' }}>
            <h3>Sorry, You need a device with a larger screen to perform editing, atleast '{'>'}=1200px'</h3>
          </div>
        </Col>
        <Col xs={0} xl={24}>
          <Layout style={{ height: '100%' }}>
            <PageHeader
              ghost={false}
              onBack={() => window.history.back()}
              title="dnde"
              subTitle=""
              style={{ borderBottom: '1px solid #e8e8e8' }}
              extra={[
                <>
                  <SendTestMail editorRef={ref} key="4" />
                  {/* <Button key="5" onClick={copyPreviewImage}>
                Copy Preview Image
              </Button> */}
                  <Button key="2" onClick={copyHTMLAsClipBoard}>
                    Copy as html
                  </Button>
                  <Button key="1" onClick={copyJsonInClipBoard}>
                    Copy as json
                  </Button>
                </>,
              ]}
            ></PageHeader>
            <Content>
              <Editor ref={ref} />
            </Content>
          </Layout>
        </Col>
      </Row>
    </div>
  );
}
Example #22
Source File: ShellReceiverPage.tsx    From yakit with GNU Affero General Public License v3.0 4 votes vote down vote up
ShellReceiverPage: React.FC<ShellReceiverPageProp> = (props) => {
    const [addrs, setAddrs] = useState<string[]>([]);
    const [loading, setLoading] = useState(false);
    const [updatingAddrs, setUpdatingAddrs] = useState(true);
    
    const waitingSyncAddr = () => {
        setUpdatingAddrs(true)
    };
    const removeListenPort = (addr: string) => {
        waitingSyncAddr()
        ipcRenderer.invoke("listening-port-cancel", addr)
    }
    const startListenPort = (addr: string) => {
        if (!addr.includes(":")) {
            failed(`无法启动端口监听程序,端口格式不合理: [${addr}]`)
            return
        }

        const result = addr.split(":", 2);
        const host = result[0];
        const port = result[1];
        if (!host || !port) {
            failed(`无法解析主机/端口`)
            return;
        }

        if (addrs.includes(addr)) {
            Modal.error({title: "该地址已经被占用: " + addr})
            failed("该地址已经被占用: " + addr)
            return;
        }

        setLoading(true)
        setTimeout(() => {
            ipcRenderer.invoke("listening-port", host, port).then(() => {
                success("监听端口成功")
            }).catch((e: any) => {
                failed(`ERROR: ${JSON.stringify(e)}`)
            }).finally(() => {
                waitingSyncAddr()
                setTimeout(() => setLoading(false), 300)
            })
        }, 500)
    };

    useEffect(() => {
        const id = setInterval(() => {
            ipcRenderer.invoke("listening-port-query-addrs").then(r => {
                setAddrs(r)
            }).finally(() => {
                if (updatingAddrs) {
                    setUpdatingAddrs(false)
                }
            })
        }, 1000)
        return () => {
            clearInterval(id)
        }
    }, [])


    const createForm = () => {
        const m = showModal({
            title: "开始监听一个 Yak 所属服务器的端口",
            width: "50%",
            content: <>
                <CreateShellReceiverForm onCheck={addr => {
                    return true
                }} onCreated={(addr) => {
                    startListenPort(addr);
                    m.destroy()
                }}/>
            </>
        })
    }

    useEffect(() => {
        const errorKey = "client-listening-port-end";
        ipcRenderer.on(errorKey, (e: any, data: any) => {
            Modal.info({title: `端口[${data}]被关闭`})
        })
        return () => {
            ipcRenderer.removeAllListeners(errorKey)
        }
    }, [])

    return <div style={{width: "100%", height: "100%", display: "flex", flexFlow: "column"}}>
        <PageHeader
            title={"Reverse Shell Receiver"}
            subTitle={
                <Space>
                    {/*<Button type={"primary"}>开启端口并监听</Button>*/}
                    <div>反弹 Shell 接收工具,可以在服务器上开启一个端口,进行监听,并进行交互。</div>
                </Space>
            }
        ></PageHeader>

        <div style={{flex: 1,overflowY: "hidden"}}>
            <AutoSpin spinning={loading || updatingAddrs}>
                <Tabs
                    className="tabs-container"
                    tabBarStyle={{marginBottom: 8}}
                    type={"editable-card"}
                    onEdit={(key, action) => {
                        if (action === "add") {
                            createForm()
                        } else if (action === "remove") {
                            removeListenPort(`${key}`)
                        }
                    }}
                >
                    {(addrs || []).length > 0 ? (
                        addrs.map((e) => {
                            return (
                                <Tabs.TabPane key={e} tab={`${e}`} closable={false}>
                                    <ShellItem addr={e} removeListenPort={removeListenPort} />
                                </Tabs.TabPane>
                            )
                        })
                    ) : (
                        <Tabs.TabPane closable={false} key={"empty"} tab={"开始监听端口"}>
                            <CreateShellReceiverForm
                                title={"开始监听:在服务器上开启一个端口"}
                                onCheck={(addr) => {
                                    return true
                                }}
                                onCreated={(addr) => {
                                    startListenPort(addr)
                                }}
                            />
                        </Tabs.TabPane>
                    )}
                </Tabs>
            </AutoSpin>
        </div>
        
    </div>
}
Example #23
Source File: ReverseServerPage.tsx    From yakit with GNU Affero General Public License v3.0 4 votes vote down vote up
ReverseServerPage: React.FC<ReverseServerPageProp> = (props) => {
    const [bridge, setBridge] = useState(false);
    const [bridgeLoading, setBridgeLoading] = useState(false);
    const [bridgeIP, setBridgeIP] = useState<string>("");
    const [bridgeAddr, setBridgeAddr] = useState("");
    const [bridgeSecret, setBridgeSecret] = useState("");


    const [params, setParams] = useState<StartFacadeServerParams>({
        ConnectParam: {
            Addr: "", Secret: "",
        },
        DNSLogLocalPort: 53,
        DNSLogRemotePort: 0,
        EnableDNSLogServer: false,
        ExternalDomain: "",
        FacadeRemotePort: 0,
        LocalFacadeHost: "0.0.0.0",
        LocalFacadePort: 4434,
        Verify: false
    });
    const [token, _] = useState(randomString(40));
    const [loading, setLoading] = useState(false);
    const [logs, setLogs, getLogs] = useGetState<ReverseNotification[]>([]);
    const [reverseToken, setReverseToken] = useState(randomString(20));


    useEffect(() => {
        const messages: ReverseNotification[] = [];
        ipcRenderer.on(`${token}-data`, (_, data: ExecResult) => {
            if (!data.IsMessage) {
                return
            }
            try {
                const message = ExtractExecResultMessage(data) as ExecResultLog;
                if (message.level !== "facades-msg") {
                    info(JSON.stringify(message))
                    return
                }
                const obj = JSON.parse(message.data) as ReverseNotification;
                obj.timestamp = message.timestamp;
                messages.unshift(obj)
                if (messages.length > 100) {
                    messages.pop()
                }
            } catch (e) {

            }

        })
        ipcRenderer.on(`${token}-error`, (e: any, data: any) => {
            if (data) {
                failed(`error: ${JSON.stringify(data)}`)
            }
        })
        ipcRenderer.on(`${token}-end`, () => {
            setLoading(false)
        })

        const id = setInterval(() => {
            if (getLogs().length !== messages.length || getLogs().length === 0) {
                setLogs([...messages])
                return
            }

            if (messages.length <= 0) {
                return
            }

            if (messages.length > 0) {
                if (messages[0].uuid !== getLogs()[0].uuid) {
                    setLogs([...messages])
                }
            }
        }, 500)
        return () => {
            clearInterval(id)
            ipcRenderer.invoke("cancel-StartFacades", token)
            ipcRenderer.removeAllListeners(`${token}-end`);
            ipcRenderer.removeAllListeners(`${token}-error`);
            ipcRenderer.removeAllListeners(`${token}-data`);
        }
    }, [token])

    const connectBridge = useMemoizedFn(() => {
        setBridgeLoading(true)
        ipcRenderer.invoke("GetTunnelServerExternalIP", {
            Addr: bridgeAddr, Secret: bridgeSecret,
        }).then((data: { IP: string }) => {
            saveValue(BRIDGE_ADDR, bridgeAddr)
            saveValue(BRIDGE_SECRET, bridgeSecret)
            setBridgeIP(data.IP)
        }).finally(() => {
            setBridgeLoading(false)
        })
    });

    // 设置 Bridge
    useEffect(() => {
        if (!bridgeAddr) {
            getValue(BRIDGE_ADDR).then((data: string) => {
                if (!!data) {
                    setBridgeAddr(`${data}`)
                }
            })
        }

        if (!bridgeSecret) {
            getValue(BRIDGE_SECRET).then((data: string) => {
                if (!!data) {
                    setBridgeSecret(`${data}`)
                }
            })
        }
    }, [])


    useEffect(() => {
        setBridgeLoading(true)
        setTimeout(() => {
            connectBridge()
        }, 500)
    }, [])

    useEffect(() => {
        if (!!bridgeIP) {
            setBridge(false)
            setParams({...params, ConnectParam: {Addr: bridgeAddr, Secret: bridgeSecret}})
        }
    }, [bridgeIP])

    return <div>
        <PageHeader
            title={"反连服务器"}
            subTitle={<Space>
                {bridgeIP ? <Tag
                    onClose={() => {
                        setBridge(true)
                        setBridgeIP("")
                    }}
                    closable={true}
                    color={"green"}>公网 <Text strong={true} style={{color: "#229900"}} copyable={true}
                >{bridgeIP}</Text></Tag> : <Form onSubmitCapture={e => e.preventDefault()}>
                    <SwitchItem size={"small"} label={"公网穿透服务"} value={bridge} setValue={setBridge}
                                formItemStyle={{marginBottom: 0}}/>
                </Form>}
                使用协议端口复用技术,同时在一个端口同时实现 HTTP / RMI / HTTPS 等协议的反连
            </Space>}
            extra={<>
                <Space>
                    {loading && <Button
                        danger={true} type={"primary"}
                        onClick={() => {
                            ipcRenderer.invoke("cancel-StartFacades", token)
                        }}
                    >关闭反连</Button>}
                </Space>
            </>}
        >
            {bridge && <Card title={"公网配置"} size={"small"}>
                <AutoSpin spinning={bridgeLoading}>
                    <Space direction={"vertical"}>
                        <Alert type={"success"} message={<Space>
                            <div>
                                在自己的服务器安装 yak 核心引擎,执行 <Text code={true} copyable={true}>yak bridge --secret
                                [your-pass]</Text> 启动
                                Yak Bridge 公网服务 <Divider type={"vertical"}/> <Text style={{color: "#999"}}>yak
                                version {`>=`} v1.0.11-sp9</Text>
                            </div>
                        </Space>}/>
                        <Form onSubmitCapture={e => {
                            e.preventDefault()

                            connectBridge()
                        }} layout={"inline"}>
                            <InputItem label={"公网 Bridge 地址"} value={bridgeAddr} setValue={setBridgeAddr}/>
                            <InputItem label={"密码"} type={"password"} value={bridgeSecret} setValue={setBridgeSecret}/>
                            <Form.Item colon={false} label={" "}>
                                <Button type="primary" htmlType="submit"> 连接公网服务器 </Button>
                            </Form.Item>
                        </Form>
                    </Space>
                </AutoSpin>
            </Card>}
            {loading && <Alert
                type={"info"}
                message={<Space direction={"vertical"}>
                    <Space>
                        本地 RMI 反连 <CopyableField
                        text={`rmi://${bridgeIP && params.ConnectParam?.Addr ? bridgeIP : "127.0.0.1"}:${params.LocalFacadePort}/${reverseToken}`}/>
                    </Space>
                    <Space>
                        本地 HTTP 反连 <CopyableField
                        text={`http://${bridgeIP && params.ConnectParam?.Addr ? bridgeIP : "127.0.0.1"}:${params.LocalFacadePort}/${reverseToken}`}/>
                    </Space>
                    <Space>
                        本地 HTTPS 反连 <CopyableField
                        text={`https://${bridgeIP && params.ConnectParam?.Addr ? bridgeIP : "127.0.0.1"}:${params.LocalFacadePort}/${reverseToken}`}/>
                    </Space>
                </Space>}>
            </Alert>}
        </PageHeader>
        <Row>
            <div style={{width: "100%"}}>
                {loading ? <>
                    <ReverseNotificationTable loading={loading} logs={logs}/>
                </> : <StartFacadeServerForm
                    params={params} setParams={setParams}
                    remoteMode={!!bridgeIP}
                    onSubmit={() => {
                        ipcRenderer.invoke("StartFacades", {
                            ...params,
                            ConnectParam: (!!bridgeIP) ? params.ConnectParam : undefined
                        } as StartFacadeServerParams, token).then(() => {
                            info("开始启动反连服务器")
                            setLoading(true)
                        })
                    }}/>}
            </div>
        </Row>
    </div>
}
Example #24
Source File: PayloadManager.tsx    From yakit with GNU Affero General Public License v3.0 4 votes vote down vote up
PayloadManagerPage: React.FC<PayloadManagerPageProp> = (props) => {
    const [groups, setGroups] = useState<string[]>([])
    const [selected, setSelected] = useState("")
    const [response, setResponse] = useState<QueryGeneralResponse<Payload>>()
    const [params, setParams] = useState<QueryPayloadParams>({
        Keyword: "",
        Group: "",
        Pagination: {Page: 1, Limit: 20, Order: "desc", OrderBy: "updated_at"}
    })
    const [selectedRows, setSelectedRows] = useState<Payload[]>([])
    const [loading, setLoading] = useState(false)
    const rowSelection = {
        selectedRowKeys: selectedRows.map((item) => item.Id),
        onChange: (selectedRowKeys, selectedRows) => setSelectedRows(selectedRows),
        fixed: true
    }
    const pagination: PaginationSchema | undefined = response?.Pagination

    const updateGroup = () => {
        ipcRenderer
            .invoke("GetAllPayloadGroup")
            .then((data: { Groups: string[] }) => {
                setGroups(data.Groups || [])
            })
            .catch((e: any) => {
                failed(e?.details || "call GetAllPayloadGroup failed")
            })
            .finally()
    }
    const updateDict = (page?: number, limit?: number) => {
        ipcRenderer
            .invoke("QueryPayload", {
                ...params,
                Group: selected,
                Pagination: {
                    ...params.Pagination,
                    Page: page || params.Pagination.Page,
                    Limit: limit || params.Pagination.Limit
                }
            } as QueryPayloadParams)
            .then((data) => {
                setResponse(data)
            })
            .catch((e: any) => {
                failed(e?.details || "query payload failed")
            })
    }
    const delDictContent = (id?: number) => {
        let params: any = {}
        if (id !== undefined) params.Id = +id
        else params.Ids = selectedRows.map((item) => +item.Id)

        ipcRenderer
            .invoke("DeletePayloadByContent", params)
            .then(() => {
                setSelectedRows([])
                updateDict()
            })
            .catch((e: any) => {
                failed("batch delete failed")
            })
    }

    useEffect(() => {
        updateGroup()
    }, [])

    useEffect(() => {
        if (!selected) {
            return
        }

        updateDict()
    }, [selected])

    return (
        <div className='payload-manager-page'>
            <PageHeader
                title={"Payload / 字典管理"}
                subTitle={`增加 / 删除 / 管理字典,可以通过 fuzz 模块 {{x(字典名)}} 来渲染`}
            />
            <Row gutter={18} style={{flexGrow: 1}}>
                <Col span={8}>
                    <AutoCard
                        title={"选择 / 查看已有字典"}
                        size={"small"} loading={loading}
                        bordered={false}
                        bodyStyle={{overflow: "auto"}}
                        extra={
                            !props.readOnly && <Form size={"small"} onSubmitCapture={(e) => e.preventDefault()}>
                                <Form.Item style={{marginBottom: 0}} label={" "} colon={false}>
                                    <Button.Group>
                                        <Button
                                            size={"small"}
                                            onClick={() => {
                                                let m = showModal({
                                                    title: "创建新的 Payload 组/字典",
                                                    content: (
                                                        <>
                                                            <CreatePayloadGroup
                                                                onLoading={() => {
                                                                    setLoading(true)
                                                                }}
                                                                onLoadingFinished={() => {
                                                                    setTimeout(() => setLoading(false), 300)
                                                                }}
                                                                Group={""}
                                                                onFinished={(e) => {
                                                                    info("创建/修改 Payload 字典/组成功")
                                                                    updateGroup()
                                                                    m.destroy()
                                                                }}
                                                            />
                                                        </>
                                                    ),
                                                    width: "60%"
                                                })
                                            }}
                                        >
                                            新增 / 扩充字典
                                        </Button>
                                        <Button
                                            size={"small"}
                                            onClick={() => {
                                                let m = showModal({
                                                    title: "上传新的 Payload 组/字典",
                                                    content: (
                                                        <>
                                                            <UploadPayloadGroup
                                                                Group={""}
                                                                onFinished={(e) => {
                                                                    info("上传 Payload 字典/组成功")
                                                                    updateGroup()
                                                                    m.destroy()
                                                                }}
                                                            />
                                                        </>
                                                    ),
                                                    width: "60%",
                                                    maskClosable: false
                                                })
                                            }}
                                        >
                                            上传字典
                                        </Button>
                                    </Button.Group>
                                </Form.Item>
                            </Form>
                        }
                    >
                        <List<string>
                            style={{height: 200}}
                            dataSource={groups}
                            renderItem={(element, index) => {
                                return (
                                    <List.Item id={index.toString()}>
                                        <Button.Group style={{width: "100%", textAlign: "left"}}>
                                            <Button
                                                style={{width: "100%", textAlign: "left"}}
                                                type={selected === element ? "primary" : undefined}
                                                onClick={(e) => setSelected(element)}
                                            >
                                                字典分组名:{element}
                                            </Button>
                                            {props.selectorHandle && <Popconfirm title={"确定要使用该字典?"}
                                                                                 onConfirm={() => {
                                                                                     props.selectorHandle && props.selectorHandle(fuzzTag(element))
                                                                                 }}
                                            >
                                                <Button type={"primary"} icon={<ThunderboltFilled/>}/>
                                            </Popconfirm>}
                                            {!props.readOnly && <Popconfirm
                                                title={"确定删除该字典吗?"}
                                                onConfirm={(e) => {
                                                    ipcRenderer
                                                        .invoke("DeletePayloadByGroup", {
                                                            Group: element
                                                        })
                                                        .then(() => {
                                                            updateGroup()
                                                            if (selected === element) {
                                                                setSelected("")
                                                                setResponse(undefined)
                                                            }
                                                        })
                                                        .catch((e: any) => {
                                                            failed("Delete Payload By Group failed")
                                                        })
                                                }}
                                            >
                                                <Button
                                                    danger={true}
                                                    icon={<DeleteOutlined/>}
                                                    type={selected === element ? "primary" : undefined}
                                                />
                                            </Popconfirm>}
                                        </Button.Group>
                                    </List.Item>
                                )
                            }}
                        />
                    </AutoCard>
                </Col>
                <Col span={16}>
                    <AutoCard
                        title={
                            <>
                                <span>字典内容</span>
                                {selectedRows.length > 0 && !props.readOnly && (
                                    <Button size='small' type='link' danger onClick={() => delDictContent()}>
                                        批量删除
                                    </Button>
                                )}
                            </>
                        }
                        size={"small"}
                        bordered={false}
                        bodyStyle={{overflow: "auto", padding: 0}}
                        extra={
                            props.readOnly ?
                                (
                                    !!props.selectorHandle ? <Button size={"small"} type={"primary"} onClick={() => {
                                        props.selectorHandle && props.selectorHandle(`{{x(${selected})}}`)
                                    }}>
                                        选择该Fuzz标签
                                    </Button> : <CopyToClipboard
                                        text={`{{x(${selected})}}`}
                                        onCopy={(text, ok) => {
                                            if (ok) success("已复制到粘贴板")
                                        }}
                                    >
                                        <Button size={"small"}>复制Fuzz标签</Button>
                                    </CopyToClipboard>
                                ) : <Form
                                    size={"small"}
                                    onSubmitCapture={(e) => {
                                        e.preventDefault()

                                        updateDict(1, 20)
                                    }}
                                    layout={"inline"}
                                    style={{marginBottom: 0}}
                                >
                                    <Form.Item style={{marginBottom: 0}}>
                                        {selected && <Tag color={"geekblue"}>{selected}</Tag>}
                                    </Form.Item>
                                    <InputItem
                                        label={"搜索"}
                                        style={{marginBottom: 0}}
                                        setValue={(Keyword) => setParams({...params, Keyword})}
                                        value={params.Keyword}
                                    />
                                    <Form.Item colon={false} label={" "} style={{marginBottom: 0}}>
                                        <Button.Group>
                                            <Button type='primary' htmlType='submit'>
                                                {" "}
                                                Search{" "}
                                            </Button>
                                            {!!props.selectorHandle ? <Button type={"primary"} onClick={() => {
                                                props.selectorHandle && props.selectorHandle(`{{x(${selected})}}`)
                                            }}>
                                                选择该Fuzz标签
                                            </Button> : <CopyToClipboard
                                                text={`{{x(${selected})}}`}
                                                onCopy={(text, ok) => {
                                                    if (ok) success("已复制到粘贴板")
                                                }}
                                            >
                                                <Button>复制Fuzz标签</Button>
                                            </CopyToClipboard>}
                                        </Button.Group>
                                    </Form.Item>
                                </Form>
                        }
                    >
                        <Table<Payload>
                            style={{height: 200}}
                            bordered={true}
                            size={"small"}
                            rowKey={(row) => row.Id}
                            rowSelection={rowSelection}
                            columns={[
                                {title: "所属字典", render: (e: Payload) => <Tag>{e.Group}</Tag>},
                                {
                                    title: "字典内容",
                                    render: (e: Payload) => (
                                        <Text style={{width: 500}} ellipsis={{tooltip: true}}>
                                            {e.Content}
                                        </Text>
                                    )
                                },
                                {
                                    title: "操作",
                                    fixed: "right",
                                    render: (e: Payload) => (
                                        <Button danger onClick={() => delDictContent(e.Id)}>
                                            删除
                                        </Button>
                                    )
                                }
                            ]}
                            onChange={(p) => {
                                updateDict(p.current, p.pageSize)
                            }}
                            pagination={{
                                size: "small",
                                pageSize: pagination?.Limit || 10,
                                total: response?.Total || 0,
                                showTotal: (i) => <Tag>共{i}条历史记录</Tag>
                            }}
                            dataSource={response?.Data}
                        />
                    </AutoCard>
                </Col>
            </Row>
        </div>
    )
}
Example #25
Source File: MITMPage.tsx    From yakit with GNU Affero General Public License v3.0 4 votes vote down vote up
MITMPage: React.FC<MITMPageProp> = (props) => {
    const [status, setStatus] = useState<"idle" | "hijacked" | "hijacking">("idle");
    const [error, setError] = useState("");
    const [host, setHost] = useState("127.0.0.1");
    const [port, setPort] = useState(8083);
    const [downstreamProxy, setDownstreamProxy] = useState<string>();
    const [loading, setLoading] = useState(false);
    const [caCerts, setCaCerts] = useState<CaCertData>({
        CaCerts: new Buffer(""), LocalFile: "",
    });
    const [enableInitialPlugin, setEnableInitialPlugin] = useState(false);

    // 存储修改前和修改后的包!
    const [currentPacketInfo, setCurrentPacketInfo] = useState<{
        currentPacket: Uint8Array,
        currentPacketId: number,
        isHttp: boolean
    }>({currentPacketId: 0, currentPacket: new Buffer([]), isHttp: true});
    const {currentPacket, currentPacketId, isHttp} = currentPacketInfo;
    const clearCurrentPacket = () => {
        setCurrentPacketInfo({currentPacketId: 0, currentPacket: new Buffer([]), isHttp: true})
    }
    const [modifiedPacket, setModifiedPacket] = useState<Uint8Array>(new Buffer([]));

    // 自动转发 与 劫持响应的自动设置
    const [autoForward, setAutoForward, getAutoForward] = useGetState<"manual" | "log" | "passive">("log");
    const isManual = autoForward === "manual";

    const [hijackAllResponse, setHijackAllResponse] = useState(false); // 劫持所有请求
    const [allowHijackCurrentResponse, setAllowHijackCurrentResponse] = useState(false); // 仅劫持一个请求
    const [initialed, setInitialed] = useState(false);
    const [forResponse, setForResponse] = useState(false);
    const [haveSideCar, setHaveSideCar] = useState(true);

    const [urlInfo, setUrlInfo] = useState("监听中...")
    const [ipInfo, setIpInfo] = useState("")

    // 设置初始化启动的插件
    const [defaultPlugins, setDefaultPlugins] = useState<string[]>([]);

    // yakit log message
    const [logs, setLogs] = useState<ExecResultLog[]>([]);
    const latestLogs = useLatest<ExecResultLog[]>(logs);
    const [_, setLatestStatusHash, getLatestStatusHash] = useGetState("");
    const [statusCards, setStatusCards] = useState<StatusCardProps[]>([])

    // filter 过滤器
    const [mitmFilter, setMITMFilter] = useState<MITMFilterSchema>();

    // 内容替代模块
    const [replacers, setReplacers] = useState<MITMContentReplacerRule[]>([]);

    // mouse
    const mouseState = useMouse();

    // 操作系统类型
    const [system, setSystem] = useState<string>()

    useEffect(() => {
        ipcRenderer.invoke('fetch-system-name').then((res) => setSystem(res))
    }, [])

    useEffect(() => {
        // 设置 MITM 初始启动插件选项
        getValue(CONST_DEFAULT_ENABLE_INITIAL_PLUGIN).then(a => {
            setEnableInitialPlugin(!!a)
        })
    }, [])

    // 用于接受后端传回的信息
    useEffect(() => {
        setInitialed(false)
        // 用于前端恢复状态
        ipcRenderer.invoke("mitm-have-current-stream").then(data => {
            const {haveStream, host, port} = data;
            if (haveStream) {
                setStatus("hijacking")
                setHost(host);
                setPort(port);
            }
        }).finally(() => {
            recover()
            setTimeout(() => setInitialed(true), 500)
        })

        // 用于启动 MITM 开始之后,接受开始成功之后的第一个消息,如果收到,则认为说 MITM 启动成功了
        ipcRenderer.on("client-mitm-start-success", () => {
            setStatus("hijacking")
            setTimeout(() => {
                setLoading(false)
            }, 300)
        })

        // 用于 MITM 的 Message (YakitLog)
        const messages: ExecResultLog[] = [];
        const statusMap = new Map<string, StatusCardProps>();
        let lastStatusHash = '';
        ipcRenderer.on("client-mitm-message", (e, data: ExecResult) => {
            let msg = ExtractExecResultMessage(data);
            if (msg !== undefined) {
                // logHandler.logs.push(msg as ExecResultLog)
                // if (logHandler.logs.length > 25) {
                //     logHandler.logs.shift()
                // }
                const currentLog = msg as ExecResultLog;
                if (currentLog.level === "feature-status-card-data") {
                    lastStatusHash = `${currentLog.timestamp}-${currentLog.data}`

                    try {
                        // 解析 Object
                        const obj = JSON.parse(currentLog.data)
                        const {id, data} = obj;
                        if (!data) {
                            statusMap.delete(`${id}`)
                        } else {
                            statusMap.set(`${id}`, {Data: data, Id: id, Timestamp: currentLog.timestamp})
                        }
                    } catch (e) {

                    }
                    return
                }
                messages.push(currentLog)
                if (messages.length > 25) {
                    messages.shift()
                }
            }
        })

        // let currentFlow: HTTPFlow[] = []
        ipcRenderer.on("client-mitm-history-update", (e: any, data: any) => {
            // currentFlow.push(data.historyHTTPFlow as HTTPFlow)
            //
            // if (currentFlow.length > 30) {
            //     currentFlow = [...currentFlow.slice(0, 30)]
            // }
            // setFlows([...currentFlow])
        })

        ipcRenderer.on("client-mitm-error", (e, msg) => {
            if (!msg) {
                info("MITM 劫持服务器已关闭")
            } else {
                failed("MITM 劫持服务器异常或被关闭")
                Modal.error({
                    mask: true, title: "启动 MITM 服务器 ERROR!",
                    content: <>{msg}</>
                })
            }
            ipcRenderer.invoke("mitm-stop-call")
            setError(`${msg}`)
            setStatus("idle")
            setTimeout(() => {
                setLoading(false)
            }, 300)
        });
        ipcRenderer.on("client-mitm-filter", (e, msg) => {
            ipcRenderer
                .invoke("get-value", DefaultMitmFilter)
                .then((res: any) => {
                    if (res) {
                        const filter = {
                            includeSuffix: res.includeSuffix,
                            excludeMethod: res.excludeMethod,
                            excludeSuffix: res.excludeSuffix,
                            includeHostname: res.includeHostname,
                            excludeHostname: res.excludeHostname,
                            excludeContentTypes: res.excludeContentTypes,
                        }
                        setMITMFilter(filter)
                        ipcRenderer.invoke("mitm-filter", {
                            updateFilter: true, ...filter
                        })
                    } else {
                        setMITMFilter({
                            includeSuffix: msg.includeSuffix,
                            excludeMethod: msg.excludeMethod,
                            excludeSuffix: msg.excludeSuffix,
                            includeHostname: msg.includeHostname,
                            excludeHostname: msg.excludeHostname,
                            excludeContentTypes: msg.excludeContentTypes,
                        })
                    }
                })
        })

        const updateLogs = () => {
            if (latestLogs.current.length !== messages.length) {
                setLogs([...messages])
                return
            }

            if (latestLogs.current.length > 0 && messages.length > 0) {
                if (latestLogs.current[0].data !== messages[0].data) {
                    setLogs([...messages])
                    return
                }
            }

            if (getLatestStatusHash() !== lastStatusHash) {
                setLatestStatusHash(lastStatusHash)

                const tmpCurrent: StatusCardProps[] = [];
                statusMap.forEach((value, key) => {
                    tmpCurrent.push(value)
                })
                setStatusCards(tmpCurrent.sort((a, b) => a.Id.localeCompare(b.Id)))
            }
        }
        updateLogs()
        let id = setInterval(() => {
            updateLogs()
        }, 1000)

        return () => {
            clearInterval(id);
            ipcRenderer.removeAllListeners("client-mitm-error")
            // ipcRenderer.invoke("mitm-close-stream")
        }
    }, [])

    useEffect(() => {
        if (hijackAllResponse && currentPacketId > 0) {
            allowHijackedResponseByRequest(currentPacketId)
        }
    }, [hijackAllResponse, currentPacketId])

    useEffect(() => {
        ipcRenderer.on("client-mitm-hijacked", forwardHandler);
        return () => {
            ipcRenderer.removeAllListeners("client-mitm-hijacked")
        }
    }, [autoForward])

    useEffect(() => {
        ipcRenderer.invoke("mitm-auto-forward", !isManual).finally(() => {
            console.info(`设置服务端自动转发:${!isManual}`)
        })
    }, [autoForward])

    useEffect(() => {
        ipcRenderer.on("client-mitm-content-replacer-update", (e, data: MITMResponse) => {
            setReplacers(data?.replacers || [])
            return
        });
        return () => {
            ipcRenderer.removeAllListeners("client-mitm-content-replacer-update")
        }
    }, [])

    useEffect(() => {
        if (currentPacketId <= 0 && status === "hijacked") {
            recover()
            const id = setInterval(() => {
                recover()
            }, 500)
            return () => {
                clearInterval(id)
            }
        }
    }, [currentPacketId])

    useEffect(() => {
        ipcRenderer.invoke("DownloadMITMCert", {}).then((data: CaCertData) => {
            setCaCerts(data)
        })
    }, [])

    const addr = `http://${host}:${port}`;

    // 自动转发劫持,进行的操作
    const forwardHandler = useMemoizedFn((e: any, msg: MITMResponse) => {
        setMITMFilter({
            includeSuffix: msg.includeSuffix,
            excludeMethod: msg.excludeMethod,
            excludeSuffix: msg.excludeSuffix,
            includeHostname: msg.includeHostname,
            excludeHostname: msg.excludeHostname,
            excludeContentTypes: msg.excludeContentTypes,
        })

        // passive 模式是 mitm 插件模式
        //    在这个模式下,应该直接转发,不应该操作数据包
        // if (passiveMode) {
        //     if (msg.forResponse) {
        //         forwardResponse(msg.responseId || 0)
        //     } else {
        //         forwardRequest(msg.id || 0)
        //     }
        //     return
        // }

        if (msg.forResponse) {
            if (!msg.response || !msg.responseId) {
                failed("BUG: MITM 错误,未能获取到正确的 Response 或 Response ID")
                return
            }
            if (!isManual) {
                forwardResponse(msg.responseId || 0)
                if (!!currentPacket) {
                    clearCurrentPacket()
                }
            } else {
                setForResponse(true)
                setStatus("hijacked")
                setCurrentPacketInfo({
                    currentPacket: msg.response,
                    currentPacketId: msg.responseId,
                    isHttp: msg.isHttps
                })
                // setCurrentPacket(new Buffer(msg.response).toString("utf8"))
                // setCurrentPacketId(msg.responseId || 0);
            }
        } else {
            if (msg.request) {
                if (!isManual) {
                    forwardRequest(msg.id)
                    if (!!currentPacket) {
                        clearCurrentPacket()
                    }
                    // setCurrentPacket(String.fromCharCode.apply(null, msg.request))
                } else {
                    setStatus("hijacked")
                    setForResponse(false)
                    // setCurrentPacket(msg.request)
                    // setCurrentPacketId(msg.id)
                    setCurrentPacketInfo({currentPacket: msg.request, currentPacketId: msg.id, isHttp: msg.isHttps})
                    setUrlInfo(msg.url)
                    ipcRenderer.invoke("fetch-url-ip", msg.url.split('://')[1].split('/')[0]).then((res) => {
                        setIpInfo(res)
                    })
                }
            }
        }
    })

    // 这个 Forward 主要用来转发修改后的内容,同时可以转发请求和响应
    const forward = useMemoizedFn(() => {
        // ID 不存在
        if (!currentPacketId) {
            return
        }

        setLoading(true);
        setStatus("hijacking");
        setAllowHijackCurrentResponse(false)
        setForResponse(false)

        if (forResponse) {
            ipcRenderer.invoke("mitm-forward-modified-response", modifiedPacket, currentPacketId).finally(() => {
                clearCurrentPacket()
                setTimeout(() => setLoading(false))
            })
        } else {
            ipcRenderer.invoke("mitm-forward-modified-request", modifiedPacket, currentPacketId).finally(() => {
                clearCurrentPacket()
                setTimeout(() => setLoading(false))
            })
        }
    })

    const recover = useMemoizedFn(() => {
        ipcRenderer.invoke("mitm-recover").then(() => {
            // success("恢复 MITM 会话成功")
        })
    })

    const start = useMemoizedFn(() => {
        setLoading(true)
        setError("")
        ipcRenderer.invoke("mitm-start-call", host, port, downstreamProxy).catch((e: any) => {
            notification["error"]({message: `启动中间人劫持失败:${e}`})
        })
    })

    const stop = useMemoizedFn(() => {
        setLoading(true)
        ipcRenderer.invoke("mitm-stop-call").then(() => {
            setStatus("idle")
        }).catch((e: any) => {
            notification["error"]({message: `停止中间人劫持失败:${e}`})
        }).finally(() => setTimeout(() => {
            setLoading(false)
        }, 300))
    })

    const hijacking = useMemoizedFn(() => {
        // setCurrentPacket(new Buffer([]));
        clearCurrentPacket()
        setLoading(true);
        setStatus("hijacking");
    })

    function getCurrentId() {
        return currentPacketId
    }

    const downloadCert = useMemoizedFn(() => {
        return <Tooltip title={'请先下载 SSL/TLS 证书'}>
            <Button
                type={"link"}
                style={{padding: '4px 6px'}}
                onClick={() => {
                    const text = `wget -e use_proxy=yes -e http_proxy=${addr} http://download-mitm-cert.yaklang.io -O yakit-mitm-cert.pem`
                    showModal({
                        title: "下载 SSL/TLS 证书以劫持 HTTPS",
                        width: "50%",
                        content: <Space direction={"vertical"} style={{width: "100%"}}>
                            <AutoCard
                                title={"证书配置"}
                                extra={<Button
                                    type={"link"}
                                    onClick={() => {
                                        saveABSFileToOpen("yakit证书.crt.pem", caCerts.CaCerts)
                                        // openABSFileLocated(caCerts.LocalFile)
                                    }}
                                >
                                    下载到本地并打开
                                </Button>} size={"small"} bodyStyle={{padding: 0}}>
                                <div style={{height: 360}}>
                                    <YakEditor bytes={true}
                                               valueBytes={caCerts.CaCerts}
                                    />
                                </div>
                            </AutoCard>
                            <Alert message={<Space>
                                在设置代理后访问:<CopyableField text={"http://download-mitm-cert.yaklang.io"}/> 可自动下载证书
                            </Space>}/>
                        </Space>
                    })
                }}
            >HTTPS 证书配置</Button>
        </Tooltip>
    })

    const contentReplacer = useMemoizedFn(() => {
        return <Button
            type={"link"} style={{padding: `4px 6px`}}
            onClick={() => {
                let m = showDrawer({
                    placement: "top", height: "50%",
                    content: (
                        <MITMContentReplacer
                            rules={replacers}
                            onSaved={rules => {
                                setReplacers(rules)
                                m.destroy()
                            }}/>
                    ),
                    maskClosable: false,
                })
            }}
        >
            匹配/标记/替换
        </Button>
    })

    const setFilter = useMemoizedFn(() => {
        return <Button type={"link"} style={{padding: '4px 6px'}}
                       onClick={() => {
                           let m = showDrawer({
                               placement: "top", height: "50%",
                               content: <>
                                   <MITMFilters
                                       filter={mitmFilter}
                                       onFinished={(filter) => {
                                           setMITMFilter({...filter})
                                           m.destroy()
                                       }}/>
                               </>
                           });
                       }}
        >过滤器</Button>
    })

    const handleAutoForward = useMemoizedFn((e: "manual" | "log" | "passive") => {
        if (!isManual) {
            info("切换为劫持自动放行模式(仅记录)")
            setHijackAllResponse(false)
        } else {
            info("切换为手动放行模式(可修改劫持)")
        }
        setAutoForward(e)
        if (currentPacket && currentPacketId) {
            forward()
        }
    })

    const execFuzzer = useMemoizedFn((value: string) => {
        ipcRenderer.invoke("send-to-tab", {
            type: "fuzzer",
            data: {isHttps: currentPacketInfo.isHttp, request: value}
        })
    })
    const execPlugin = useMemoizedFn((value: string) => {
        ipcRenderer.invoke("send-to-packet-hack", {
            request: currentPacketInfo.currentPacket,
            ishttps: currentPacketInfo.isHttp
        })
    })

    const shiftAutoForwardHotkey = useHotkeys('ctrl+t', () => {
        handleAutoForward(isManual ? "manual" : "log")
    }, [autoForward])

    if (!initialed) {
        return <div style={{textAlign: "center", paddingTop: 120}}>
            <Spin spinning={true} tip={"正在初始化 MITM"}/>
        </div>
    }
    return <div style={{height: "100%", width: "100%"}}>
        {(() => {
            switch (status) {
                case "idle":
                    return <Spin spinning={loading}>
                        <Form
                            style={{marginTop: 40}}
                            onSubmitCapture={e => {
                                e.preventDefault()
                                start()

                                if (enableInitialPlugin) {
                                    enableMITMPluginMode(defaultPlugins).then(() => {
                                        info("被动扫描插件模式已启动")
                                    })
                                }
                            }}
                            layout={"horizontal"} labelCol={{span: 7}}
                            wrapperCol={{span: 13}}
                        >
                            <Item label={"劫持代理监听主机"}>
                                <Input value={host} onChange={e => setHost(e.target.value)}/>
                            </Item>
                            <Item label={"劫持代理监听端口"}>
                                <InputNumber value={port} onChange={e => setPort(e)}/>
                            </Item>
                            {/*<SwitchItem label={"启动 MITM 插件"} size={"small"} setValue={e => {*/}
                            {/*    setEnableInitialPlugin(e)*/}
                            {/*    if (e) {*/}
                            {/*        saveValue(CONST_DEFAULT_ENABLE_INITIAL_PLUGIN, "true")*/}
                            {/*    } else {*/}
                            {/*        saveValue(CONST_DEFAULT_ENABLE_INITIAL_PLUGIN, "")*/}
                            {/*    }*/}
                            {/*}} value={enableInitialPlugin}/>*/}
                            <Item label={"选择插件"} colon={true}>
                                <div style={{height: 200, maxWidth: 420}}>
                                    <SimplePluginList
                                        disabled={!enableInitialPlugin}
                                        bordered={true}
                                        initialSelected={defaultPlugins}
                                        onSelected={(list: string[]) => {
                                            setDefaultPlugins(list)
                                        }} pluginTypes={"mitm,port-scan"}
                                        verbose={<div>MITM 与 端口扫描插件</div>}/>
                                </div>
                            </Item>
                            <Item label={"下游代理"} help={"为经过该 MITM 代理的请求再设置一个代理,通常用于访问中国大陆无法访问的网站或访问特殊网络/内网,也可用于接入被动扫描"}>
                                <Input value={downstreamProxy} onChange={e => setDownstreamProxy(e.target.value)}/>
                            </Item>
                            <Item label={"内容规则"} help={"使用规则进行匹配、替换、标记、染色,同时配置生效位置"}>
                                <Space>
                                    <Button
                                        onClick={() => {
                                            let m = showDrawer({
                                                placement: "top", height: "50%",
                                                content: (
                                                    <MITMContentReplacerViewer/>
                                                ),
                                                maskClosable: false,
                                            })
                                        }}
                                    >已有规则</Button>
                                    <Button type={"link"} onClick={() => {
                                        const m = showModal({
                                            title: "从 JSON 中导入",
                                            width: "60%",
                                            content: (
                                                <>
                                                    <MITMContentReplacerImport onClosed={() => {
                                                        m.destroy()
                                                    }}/>
                                                </>
                                            )
                                        })
                                    }}>从 JSON 导入</Button>
                                    <Button type={"link"} onClick={() => {
                                        showModal({
                                            title: "导出配置 JSON",
                                            width: "50%",
                                            content: (
                                                <>
                                                    <MITMContentReplacerExport/>
                                                </>
                                            )
                                        })
                                    }}>导出为 JSON</Button>
                                </Space>
                            </Item>
                            <Item label={" "} colon={false}>
                                <Space>
                                    <Button type={"primary"} htmlType={"submit"}>
                                        劫持启动
                                    </Button>
                                    <Divider type={"vertical"}/>
                                    <Checkbox
                                        checked={enableInitialPlugin}
                                        onChange={node => {
                                            const e = node.target.checked;
                                            setEnableInitialPlugin(e)
                                            if (e) {
                                                saveValue(CONST_DEFAULT_ENABLE_INITIAL_PLUGIN, "true")
                                            } else {
                                                saveValue(CONST_DEFAULT_ENABLE_INITIAL_PLUGIN, "")
                                            }
                                        }}
                                    >
                                        插件自动加载
                                    </Checkbox>
                                </Space>
                            </Item>
                        </Form>
                    </Spin>
                case "hijacking":
                case "hijacked":
                    return <div id={"mitm-hijacking-container"} ref={shiftAutoForwardHotkey as Ref<any>} tabIndex={-1}
                                style={{marginLeft: 12, marginRight: 12, height: "100%"}}>
                        <Row gutter={14} style={{height: "100%"}}>
                            <Col span={haveSideCar ? 24 : 24}
                                 style={{display: "flex", flexDirection: "column", height: "100%"}}>
                                <PageHeader
                                    className="mitm-header-title"
                                    title={'劫持 HTTP Request'} subTitle={`http://${host}:${port}`}
                                    style={{marginRight: 0, paddingRight: 0, paddingTop: 0, paddingBottom: 8}}
                                    extra={
                                        <Space>
                                            <ChromeLauncherButton host={host} port={port}/>
                                            {contentReplacer()}
                                            {setFilter()}
                                            {downloadCert()}
                                            <Button danger={true} type={"link"}
                                                    onClick={() => {
                                                        stop()
                                                        setUrlInfo("监听中...")
                                                        setIpInfo("")
                                                    }} icon={<PoweroffOutlined/>}
                                            />
                                        </Space>}>
                                    <Row>
                                        <Col span={12}>
                                            <div style={{width: "100%", textAlign: "left"}}>
                                                <Space>
                                                    <Button
                                                        type={"primary"}
                                                        disabled={status === "hijacking"}
                                                        onClick={() => {
                                                            forward()
                                                        }}>提交数据</Button>
                                                    <Button
                                                        disabled={status === "hijacking"}
                                                        danger={true}
                                                        onClick={() => {
                                                            hijacking()
                                                            if (forResponse) {
                                                                dropResponse(currentPacketId).finally(() => {
                                                                    setTimeout(() => {
                                                                        setLoading(false)
                                                                    }, 300)
                                                                })
                                                            } else {
                                                                dropRequest(currentPacketId).finally(() => {
                                                                    setTimeout(() => setLoading(false), 300)
                                                                })
                                                            }
                                                            setUrlInfo("监听中...")
                                                            setIpInfo("")
                                                        }}>丢弃请求</Button>
                                                    {
                                                        (!forResponse && !!currentPacket) &&  // 劫持到的请求有内容
                                                        status === "hijacked" && // 劫持到的状态是 hijacked
                                                        !hijackAllResponse && // 如果已经设置了劫持所有请求,就不展示了
                                                        <Button
                                                            disabled={allowHijackCurrentResponse}
                                                            type={allowHijackCurrentResponse ? "primary" : "default"}
                                                            onClick={() => {
                                                                if (!allowHijackCurrentResponse) {
                                                                    allowHijackedResponseByRequest(currentPacketId)
                                                                    setAllowHijackCurrentResponse(true)
                                                                } else {
                                                                    setAllowHijackCurrentResponse(false)
                                                                }
                                                            }}>
                                                            劫持响应 {
                                                            allowHijackCurrentResponse &&
                                                            <CheckOutlined/>
                                                        }
                                                        </Button>}
                                                </Space>
                                            </div>
                                        </Col>
                                        <Col span={12}>
                                            <div style={{width: "100%", textAlign: "right"}}>
                                                <Space>
                                                    {isManual && <div>
                                                        <span style={{marginRight: 4}}>劫持响应:</span>
                                                        <Checkbox checked={hijackAllResponse} onClick={e => {
                                                            if (!hijackAllResponse) {
                                                                info("劫持所有响应内容")
                                                            } else {
                                                                info("仅劫持请求")
                                                            }
                                                            setHijackAllResponse(!hijackAllResponse)
                                                        }}/>
                                                    </div>}
                                                    <SelectOne
                                                        data={[
                                                            {text: "手动劫持", value: "manual"},
                                                            {text: "自动放行", value: "log"},
                                                            {text: "被动日志", value: "passive"},
                                                        ]}
                                                        value={autoForward}
                                                        formItemStyle={{marginBottom: 0}}
                                                        setValue={(e) => {
                                                            ipcRenderer.invoke("mitm-filter", {updateFilter: true, ...mitmFilter})
                                                            handleAutoForward(e)
                                                        }}
                                                    />
                                                </Space>
                                            </div>
                                        </Col>
                                    </Row>
                                    <Row>
                                        <Col span={12}>
                                            <div style={{
                                                width: "100%", textAlign: "left", height: '100%',
                                                display: 'flex'
                                            }}>
                                                {!isManual &&
                                                <Text style={{alignSelf: 'center'}}>
                                                    {`目标:自动放行中...`}</Text>}

                                                {autoForward === "manual" &&
                                                <>
                                                    <Text title={urlInfo} ellipsis={true} style={{
                                                        alignSelf: 'center',
                                                        maxWidth: 300
                                                    }}>{status === 'hijacking' ? '目标:监听中...' : `目标:${urlInfo}`}</Text>
                                                    {ipInfo && status !== 'hijacking' &&
                                                    <Tag
                                                        color='green'
                                                        title={ipInfo}
                                                        style={{
                                                            marginLeft: 5,
                                                            alignSelf: "center",
                                                            maxWidth: 140,
                                                            cursor: "pointer"
                                                        }}
                                                    >
                                                        {`${ipInfo}`}
                                                        <CopyToClipboard
                                                            text={`${ipInfo}`}
                                                            onCopy={(text, ok) => {
                                                                if (ok) success("已复制到粘贴板")
                                                            }}
                                                        >
                                                            <CopyOutlined style={{marginLeft: 5}}/>
                                                        </CopyToClipboard>
                                                    </Tag>
                                                    }
                                                </>
                                                }
                                            </div>
                                        </Col>
                                        <Col span={12}>
                                            <div style={{width: "100%", textAlign: "right"}}>
                                                <Button
                                                    type={"link"} onClick={() => recover()}
                                                    icon={<ReloadOutlined/>}
                                                >恢复请求</Button>
                                            </div>
                                        </Col>
                                    </Row>
                                </PageHeader>
                                <div style={{flex: 1, overflowY: 'hidden'}}>
                                    {/*<Spin wrapperClassName={"mitm-loading-spin"} spinning={status === "hijacking"}>*/}
                                    <div style={{height: "100%"}}>
                                        <ResizeBox
                                            isVer={false}
                                            firstNode={(
                                                <MITMPluginList
                                                    proxy={`http://${host}:${port}`}
                                                    downloadCertNode={downloadCert}
                                                    setFilterNode={setFilter}
                                                    onExit={() => {
                                                        stop()
                                                    }}
                                                    onSubmitScriptContent={e => {
                                                        ipcRenderer.invoke("mitm-exec-script-content", e)
                                                    }}
                                                    onSubmitYakScriptId={(id: number, params: YakExecutorParam[]) => {
                                                        info(`加载 MITM 插件[${id}]`)
                                                        ipcRenderer.invoke("mitm-exec-script-by-id", id, params)
                                                    }}
                                                />
                                                // <MITMPluginOperator />
                                            )}
                                            firstMinSize={"330px"}
                                            secondMinSize={"340px"}
                                            firstRatio={"330px"}
                                            secondNode={(
                                                <AutoCard
                                                    style={{margin: 0, padding: 0}}
                                                    bodyStyle={{margin: 0, padding: 0, overflowY: "hidden"}}
                                                >
                                                    {autoForward === "log" && (
                                                        <MITMPluginCard
                                                            onSubmitScriptContent={(e) => {
                                                                ipcRenderer.invoke("mitm-exec-script-content", e)
                                                            }}
                                                            onSubmitYakScriptId={(
                                                                id: number,
                                                                params: YakExecutorParam[]
                                                            ) => {
                                                                info(`加载 MITM 插件[${id}]`)
                                                                ipcRenderer.invoke("mitm-exec-script-by-id", id, params)
                                                            }}
                                                        />
                                                    )}
                                                    {autoForward === "manual" && (
                                                        <HTTPPacketEditor
                                                            originValue={currentPacket}
                                                            noHeader={true}
                                                            bordered={false}
                                                            onChange={setModifiedPacket}
                                                            noPacketModifier={true}
                                                            readOnly={status === "hijacking"}
                                                            refreshTrigger={
                                                                (forResponse ? `rsp` : `req`) + `${currentPacketId}`
                                                            }
                                                            actions={[
                                                                // {
                                                                //     id: "send-to-scan-packet", label: "发送到数据包扫描器",
                                                                //     run: e => {
                                                                //         // console.info(mouseState)
                                                                //         scanPacket(mouseState, false, "GET / HTTP/1.1\r\nHost: www.baidu.com", "")
                                                                //     }, contextMenuGroupId: "Scanners",
                                                                // },
                                                                ...(forResponse
                                                                    ? [
                                                                        {
                                                                            id: "trigger-auto-hijacked",
                                                                            label: "切换为自动劫持模式",
                                                                            keybindings: [
                                                                                monaco.KeyMod.Shift |
                                                                                (system === "Darwin" ? monaco.KeyMod.WinCtrl : monaco.KeyMod.CtrlCmd) |
                                                                                monaco.KeyCode.KEY_T
                                                                            ],
                                                                            run: () => {
                                                                                handleAutoForward(getAutoForward() === "manual" ? "log" : "manual")
                                                                            },
                                                                            contextMenuGroupId: "Actions"
                                                                        },
                                                                        {
                                                                            id: "forward-response",
                                                                            label: "放行该 HTTP Response",
                                                                            run: function () {
                                                                                forward()
                                                                                // hijacking()
                                                                                // forwardResponse(getCurrentId()).finally(() => {
                                                                                //     setTimeout(() => setLoading(false), 300)
                                                                                // })
                                                                            },
                                                                            contextMenuGroupId: "Actions"
                                                                        },
                                                                        {
                                                                            id: "drop-response",
                                                                            label: "丢弃该 HTTP Response",
                                                                            run: function () {
                                                                                hijacking()
                                                                                dropResponse(getCurrentId()).finally(
                                                                                    () => {
                                                                                        setTimeout(
                                                                                            () => setLoading(false),
                                                                                            300
                                                                                        )
                                                                                    }
                                                                                )
                                                                            },
                                                                            contextMenuGroupId: "Actions"
                                                                        }
                                                                    ]
                                                                    : [
                                                                        {
                                                                            id: "trigger-auto-hijacked",
                                                                            label: "切换为自动劫持模式",
                                                                            keybindings: [
                                                                                monaco.KeyMod.Shift |
                                                                                (system === "Darwin" ? monaco.KeyMod.WinCtrl : monaco.KeyMod.CtrlCmd) |
                                                                                monaco.KeyCode.KEY_T
                                                                            ],
                                                                            run: () => {
                                                                                handleAutoForward(getAutoForward() === "manual" ? "log" : "manual")
                                                                            },
                                                                            contextMenuGroupId: "Actions"
                                                                        },
                                                                        {
                                                                            id: "send-to-fuzzer",
                                                                            label: "发送到 Web Fuzzer",
                                                                            keybindings: [
                                                                                monaco.KeyMod.Shift |
                                                                                (system === "Darwin" ? monaco.KeyMod.WinCtrl : monaco.KeyMod.CtrlCmd) |
                                                                                monaco.KeyCode.KEY_R
                                                                            ],
                                                                            run: function (StandaloneEditor: any) {
                                                                                execFuzzer(StandaloneEditor.getModel().getValue())
                                                                            },
                                                                            contextMenuGroupId: "Actions"
                                                                        },
                                                                        {
                                                                            id: "send-to-plugin",
                                                                            label: "发送到 数据包扫描",
                                                                            keybindings: [
                                                                                monaco.KeyMod.Shift |
                                                                                (system === "Darwin" ? monaco.KeyMod.WinCtrl : monaco.KeyMod.CtrlCmd) |
                                                                                monaco.KeyCode.KEY_E
                                                                            ],
                                                                            run: function (StandaloneEditor: any) {
                                                                                if (!StandaloneEditor.getModel().getValue()) return
                                                                                execPlugin(StandaloneEditor.getModel().getValue())
                                                                            },
                                                                            contextMenuGroupId: "Actions"
                                                                        },
                                                                        {
                                                                            id: "forward-response",
                                                                            label: "放行该 HTTP Request",
                                                                            keybindings: [
                                                                                monaco.KeyMod.Shift |
                                                                                (system === "Darwin" ? monaco.KeyMod.WinCtrl : monaco.KeyMod.CtrlCmd) |
                                                                                monaco.KeyCode.KEY_F
                                                                            ],
                                                                            run: function () {
                                                                                forward()
                                                                                // hijacking()
                                                                                // forwardRequest(getCurrentId()).finally(() => {
                                                                                //     setTimeout(() => setLoading(false), 300)
                                                                                // })
                                                                            },
                                                                            contextMenuGroupId: "Actions"
                                                                        },
                                                                        {
                                                                            id: "drop-response",
                                                                            label: "丢弃该 HTTP Request",
                                                                            run: function () {
                                                                                hijacking()
                                                                                dropRequest(getCurrentId()).finally(
                                                                                    () => {
                                                                                        setTimeout(
                                                                                            () => setLoading(false),
                                                                                            300
                                                                                        )
                                                                                    }
                                                                                )
                                                                            },
                                                                            contextMenuGroupId: "Actions"
                                                                        },
                                                                        {
                                                                            id: "hijack-current-response",
                                                                            label: "劫持该 Request 对应的响应",
                                                                            run: function () {
                                                                                allowHijackedResponseByRequest(
                                                                                    getCurrentId()
                                                                                )
                                                                            },
                                                                            contextMenuGroupId: "Actions"
                                                                        }
                                                                    ])
                                                            ]}
                                                        />
                                                    )}
                                                    {autoForward === "passive" && (
                                                        <MITMPluginLogViewer
                                                            messages={logs} status={statusCards}
                                                        />
                                                    )}
                                                </AutoCard>
                                            )}
                                        />
                                    </div>
                                    {/*</Spin>*/}
                                </div>
                            </Col>
                        </Row>
                    </div>
                default:
                    return <div/>
            }
        })()}
    </div>
}
Example #26
Source File: YakBatchExecutorLegacy.tsx    From yakit with GNU Affero General Public License v3.0 4 votes vote down vote up
YakBatchExecutorLegacy: React.FC<YakBatchExecutorProp> = (props) => {
    const [params, setParams] = useState<ExecBatchYakScriptParams>({
        Concurrent: 5,
        Keyword: props.keyword,
        DisableNucleiWorkflow: true,
        Limit: 100,
        Target: "",
        TotalTimeoutSeconds: 180,
        Type: "nuclei"
    });
    const [totalLoading, setTotalLoading] = useState(true);
    const [tasks, setTasks] = useState<ExecBatchYakScriptTask[]>([]);
    const [error, setError] = useState("");
    const [token, setToken] = useState("");
    const [executing, setExecuting] = useState(false);

    useEffect(() => {
        setTotalLoading(true)
        setTimeout(() => setTotalLoading(false), 500)

        const token = randomString(40);
        setToken(token);
        setTasks([]);
        setParams({...params, Keyword: props.keyword, Target: ""})
        const tempTasks = new Map<string, ExecBatchYakScriptTask>();
        const updateTasks = () => {
            let items: ExecBatchYakScriptTask[] = [];
            tempTasks.forEach((v, k) => {
                items.push(v)
            })
            setTasks(items.sort((a, b) => b.Id.localeCompare(a.Id)))
        }
        const dataChannel = `${token}-exec-batch-yak-script-data`;
        const errorChannel = `${token}-exec-batch-yak-script-error`;
        const endChannel = `${token}-exec-batch-yak-script-end`;

        let updateTableTick = setInterval(updateTasks, 1000);
        ipcRenderer.on(dataChannel, async (e: any, data: ExecBatchYakScriptResult) => {
            if (data.ProgressMessage) {
                return
            }

            let element = tempTasks.get(data.Id);
            if (element === undefined) {
                tempTasks.set(data.Id, {
                    Id: data.Id,
                    PoC: data.PoC,
                    Results: [],
                    Status: data.Status,
                    progress: 0,
                })
                // updateTasks()
                return
            } else {
                element.Status = data.Status

                if (!element.Ok) {
                    element.Ok = data.Ok || false
                }
                element.Reason = data.Reason

                if (data.Result) {
                    element.Results.push({...data.Result})
                }
                // updateTasks()
                return
            }
        })
        ipcRenderer.on(errorChannel, (e: any, error: any) => {
            setError(error)
        })
        ipcRenderer.on(endChannel, (e: any, data: any) => {
            info("模块加载完成 / 执行完毕")
            setExecuting(false)
            updateTasks()
        })
        ipcRenderer.invoke("exec-batch-yak-script", {...params, Keyword: props.keyword, Target: ""}, token)
        setExecuting(true)
        return () => {
            clearInterval(updateTableTick);
            ipcRenderer.invoke("cancel-exec-batch-yak-script", token)
            ipcRenderer.removeAllListeners(dataChannel)
            ipcRenderer.removeAllListeners(errorChannel)
            ipcRenderer.removeAllListeners(endChannel)
        }
    }, [props.keyword])

    if (totalLoading) {
        return <div style={{textAlign: "center", width: "100%", marginTop: 100}}>
            <Spin>正在加载专用漏洞库</Spin>
        </div>
    }

    return <div>
        <PageHeader
            title={`漏洞与风险监测专题:${props.verbose ? props.verbose : props.keyword}`}
            style={{width: "100%"}}
        >
            <div style={{textAlign: "center", width: "100%"}}>
                <Form style={{
                    textAlign: "center",
                }} onSubmitCapture={e => {
                    e.preventDefault()

                    if (!params.Target) {
                        Modal.error({title: "检测目标不能为空"})
                        return
                    }

                    if (!params.Keyword) {
                        Modal.error({title: "无 PoC 关键字选择"})
                        return
                    }

                    if (!token) {
                        Modal.error({title: "BUG:无 Token 生成,请重新打开该页"})
                    }

                    ipcRenderer.invoke("exec-batch-yak-script", params, token)
                    setExecuting(true)
                }}>
                    <Space direction={"vertical"}>
                        <Space>
                            <span>输入想要检测的目标:</span>
                            <Form.Item
                                style={{marginBottom: 0}}
                            >
                                <Input
                                    style={{width: 600}}
                                    value={params.Target}
                                    onChange={e => {
                                        setParams({...params, Target: e.target.value})
                                    }}
                                    suffix={<Space>
                                        {!executing ? <Button style={{width: 120}} type="primary"
                                                              htmlType="submit"
                                        > 开始检测 </Button> : <Popconfirm
                                            title={"确定要停止该漏洞检测?"}
                                            onConfirm={e => {
                                                ipcRenderer.invoke("cancel-exec-batch-yak-script", token)
                                            }}
                                        >
                                            <Button style={{width: 120}} danger={true}> 强制停止 </Button>
                                        </Popconfirm>}
                                    </Space>}
                                />
                            </Form.Item>
                            <Button type={"link"} style={{margin: 0, paddingLeft: 0}} onClick={e => {
                                showModal({
                                    title: "设置批量检测额外参数",
                                    content: <>
                                        <Form
                                            onSubmitCapture={e => e.preventDefault()}
                                            labelCol={{span: 7}} wrapperCol={{span: 14}}>
                                            <InputInteger
                                                label={"并发量(线程)"}
                                                setValue={Concurrent => setParams({...params, Concurrent})}
                                                defaultValue={params.Concurrent}
                                            />
                                            <InputInteger
                                                label={"限制模块数量"}
                                                setValue={Limit => setParams({...params, Limit})}
                                                defaultValue={params.Limit}
                                            />
                                            <InputInteger
                                                label={"总超时时间/s"}
                                                setValue={TotalTimeoutSeconds => setParams({
                                                    ...params,
                                                    TotalTimeoutSeconds
                                                })}
                                                defaultValue={params.TotalTimeoutSeconds}
                                            />
                                        </Form>
                                    </>
                                })
                            }}>额外参数</Button>
                        </Space>
                        <div style={{width: "100%", textAlign: "right"}}>
                            <Space direction={"vertical"}>
                                <Space>
                                    <Tag>并发/线程: {params.Concurrent}</Tag>
                                    <Tag>总超时: {params.TotalTimeoutSeconds} sec</Tag>
                                    <Tag>限制模块最大数: {params.Limit}</Tag>
                                    <p style={{color: "#999", marginBottom: 0}}>
                                        可接受输入为:URL / IP / 域名 / 主机:端口
                                    </p>
                                    <div style={{width: 80}}/>
                                </Space>
                            </Space>
                        </div>
                    </Space>
                </Form>
            </div>
        </PageHeader>
        <Divider/>
        <Row gutter={12} style={{height: "100%"}}>
            <div
                style={{
                    width: "100%", textAlign: "center",
                    marginLeft: 20, marginRight: 20,
                    marginBottom: 100,
                }}
            >
                <Table<ExecBatchYakScriptTask>
                    pagination={false}
                    dataSource={tasks}
                    bordered={true} size={"small"}
                    rowKey={(row) => {
                        return row.Id
                    }}
                    columns={[
                        {
                            title: "模块名称",
                            width: 400,
                            render: (i: ExecBatchYakScriptTask) => <div style={{overflow: "auto", width: 400}}>
                                <Text
                                    ellipsis={{tooltip: true}} copyable={true} style={{width: 300}}
                                >{i.Id}</Text>
                            </div>
                        },
                        {
                            title: "模块状态", width: 150,
                            render: (i: ExecBatchYakScriptTask) => StatusToVerboseTag(i.Status)
                        },
                        {
                            title: "执行过程预览", render: (i: ExecBatchYakScriptTask) => {
                                return <ExecResultsViewer results={i.Results} oneLine={true}/>
                            }
                        },
                        {
                            title: "操作",
                            render: (i: ExecBatchYakScriptTask) => <div>
                                <Space>
                                    <Button
                                        type={"primary"} size={"small"}
                                        onClick={e => {
                                            if (!i.PoC) {
                                                Modal.error({title: "没有模块信息"})
                                                return
                                            }
                                            showModal({
                                                title: `单体模块测试: ${i.PoC.ScriptName}`,
                                                width: "75%",
                                                content: <>
                                                    <YakScriptOperator script={i.PoC}/>
                                                </>
                                            })
                                        }}
                                    >单独检测</Button>
                                    <Button
                                        size={"small"}
                                        onClick={e => {
                                            if (!i.PoC) {
                                                Modal.error({title: "没有模块信息"})
                                                return
                                            }
                                            showModal({
                                                title: `源码: ${i.PoC.ScriptName}`,
                                                width: "75%",
                                                content: <>
                                                    <div style={{height: 400}}>
                                                        <YakEditor
                                                            readOnly={true} type={"yaml"} value={i.PoC.Content}
                                                        />
                                                    </div>
                                                </>
                                            })
                                        }}
                                    >源码</Button>
                                    <Button type={"primary"} size={"small"} disabled={true}>待开发...</Button>
                                </Space>
                            </div>
                        },
                    ]}
                >

                </Table>
            </div>
        </Row>
    </div>
}
Example #27
Source File: YakScriptManager.tsx    From yakit with GNU Affero General Public License v3.0 4 votes vote down vote up
YakScriptManagerPage: React.FC<YakScriptManagerPageProp> = (props) => {
    const [response, setResponse] = useState<QueryYakScriptsResponse>({
        Data: [], Pagination: {
            Limit: props.limit || 15, Page: 1,
            Order: "desc", OrderBy: "updated_at"
        },
        Total: 0
    });
    const [selectedScript, setSelectedScript] = useState<YakScript>();
    const {Data, Pagination, Total} = response;
    const [params, setParams] = useState<QueryYakScriptRequest>({
        Pagination: {
            Limit: props.limit || 15, Page: 1,
            Order: "desc", OrderBy: "updated_at"
        }, Type: props.type || undefined,
        Keyword: props.keyword || "", IsHistory: false
    });
    const [loading, setLoading] = useState(false);

    const isMainPage = !props.onLoadYakScript

    const update = (page?: number, limit?: number) => {
        const newParams = {
            ...params
        }
        if (page) newParams.Pagination.Page = page;
        if (limit) newParams.Pagination.Limit = limit;
        setLoading(true)

        ipcRenderer.invoke("QueryYakScript", newParams).then((data: QueryYakScriptsResponse) => {
            setResponse(data)
        }).finally(() => setTimeout(() => setLoading(false), 300))
    };

    useEffect(() => {
        update(1)
    }, [params.Type])

    const renderTable = () => {
        return <Space direction={"vertical"} style={{width: "100%"}}>
            {!props.onlyViewer && <Form onSubmitCapture={e => {
                e.preventDefault()
                update(1)
            }} layout={"inline"}>
                <InputItem
                    label={"搜索关键字"}
                    setValue={Keyword => setParams({...params, Keyword})}
                    value={params.Keyword}
                />
                <Form.Item colon={false}>
                    <Button.Group>
                        <Button type="primary" htmlType="submit">搜索</Button>
                        <Button onClick={e => {
                            if (!params.Keyword) {
                                Modal.error({title: "关键字为空无法生成批量扫描能力"});
                                return
                            }
                            showDrawer({
                                title: "", width: "93%", mask: false, keyboard: false,
                                content: <>
                                    <YakBatchExecutorLegacy
                                        keyword={params.Keyword || ""}
                                        verbose={`自定义搜索关键字: ${params.Keyword}`}
                                    />
                                </>,
                            })
                        }}>批量</Button>
                    </Button.Group>
                </Form.Item>
            </Form>}
            <Table<YakScript>
                size={"small"}
                dataSource={Data}
                rowKey={"Id"}
                loading={loading} bordered={true}
                scroll={{y: 750}}
                expandable={{
                    expandedRowRender: (i: YakScript) => {
                        return <div style={{height: 400}}>
                            <YakEditor
                                type={"yak"} readOnly={true} value={i.Content}
                            />
                        </div>
                    },
                }}
                onRow={isMainPage ? r => {
                    return {
                        onClick: () => {
                            setSelectedScript(r)
                        }
                    }
                } : undefined}
                pagination={{
                    size: "small",
                    pageSize: Pagination?.Limit || 10,
                    total: Total,
                    showTotal: (i) => <Tag>共{i}条历史记录</Tag>,
                    // onChange(page: number, limit?: number): any {
                    //     update(page, limit)
                    // },
                }}
                onChange={(p) => {
                    update(p.current, p.pageSize)
                }}
                columns={isMainPage ? [
                    {
                        title: "模块名称", width: 300,
                        render: (i: YakScript) => <Tag><Text
                            style={{maxWidth: 260}} copyable={true}
                            ellipsis={{tooltip: true}}>
                            {i.ScriptName}
                        </Text></Tag>
                    },
                    // {
                    //     title: "描述", render: (i: YakScript) => <Text
                    //         style={{maxWidth: 300}}
                    //         ellipsis={{tooltip: true}}
                    //     >{i.Help}</Text>, width: 330,
                    // },
                    {
                        title: "操作", fixed: "right", width: 135, render: (i: YakScript) => <Space>
                            <Button size={"small"} onClick={e => {
                                let m = showDrawer({
                                    title: "修改当前 Yak 模块", width: "90%", keyboard: false,
                                    content: <>
                                        <YakScriptCreatorForm
                                            modified={i} onChanged={i => update()}
                                            onCreated={(created) => {
                                                m.destroy()
                                            }}
                                        />
                                    </>
                                })
                            }}>修改</Button>
                            <Popconfirm
                                title={"确认想要删除该模块?"}
                                onConfirm={e => {
                                    ipcRenderer.invoke("delete-yak-script", i.Id)
                                    setLoading(true)
                                    setTimeout(() => update(1), 1000)
                                }}
                            >
                                <Button size={"small"} danger={true}>删除</Button>
                            </Popconfirm>
                        </Space>
                    },
                ] : [
                    {
                        title: "模块名称", fixed: "left",
                        render: (i: YakScript) => <Tag><Text style={{maxWidth: 200}} copyable={true}
                                                             ellipsis={{tooltip: true}}>
                            {i.ScriptName}
                        </Text></Tag>
                    },
                    {
                        title: "描述", render: (i: YakScript) => <Text
                            style={{maxWidth: 200}}
                            ellipsis={{tooltip: true}}
                        >{i.Help}</Text>
                    },
                    {
                        title: "操作", fixed: "right", render: (i: YakScript) => <Space>
                            {props.onLoadYakScript && <Button size={"small"} onClick={e => {
                                props.onLoadYakScript && props.onLoadYakScript(i)
                            }} type={"primary"}>加载</Button>}
                        </Space>
                    },
                ]}
            />
        </Space>
    }

    return <div>
        {!props.onlyViewer && <PageHeader
            title={"Yak 模块管理器"}
            subTitle={<Space>
                <Button
                    icon={<ReloadOutlined/>}
                    type={"link"}
                    onClick={() => {
                        update()
                    }}
                />
                {props.type ? undefined : <Form layout={"inline"}>
                    <ManySelectOne
                        formItemStyle={{marginBottom: 0, width: 200}}
                        label={"模块类型"}
                        data={[
                            {value: "yak", text: "Yak 原生模块"},
                            {value: "nuclei", text: "nuclei Yaml模块"},
                            {value: undefined, text: "全部"},
                        ]}
                        setValue={Type => setParams({...params, Type})} value={params.Type}
                    />
                </Form>}
                <div>
                    你可以在这里管理 / 添加你的 Yak 模块
                </div>
            </Space>}
            extra={[
                isMainPage ? <Popconfirm
                    title={<>
                        确定要加载本地 yaml(nuclei) poc 吗?<br/>
                        可通过 <Text mark={true} copyable={true}>yak update-nuclei-poc</Text> 一键更新已知 PoC
                    </>}
                    onConfirm={() => {
                        ipcRenderer.invoke("update-nuclei-poc")
                    }}
                >
                    <Button>加载 PoC(nuclei)</Button>
                </Popconfirm> : undefined,
                <Button type={"primary"} onClick={e => {
                    let m = showDrawer({
                        title: "创建新的 Yakit 模块",
                        keyboard: false,
                        width: "95%",
                        content: <>
                            <YakScriptCreatorForm onCreated={() => {
                                m.destroy()
                            }} onChanged={e => update(1)}/>
                        </>
                    })
                }}>创建新脚本</Button>
            ]}
        />}
        {(isMainPage && !props.onlyViewer) ? <Row gutter={12}>
            <Col span={8}>
                {renderTable()}
            </Col>
            <Col span={16}>
                {selectedScript ? <YakScriptOperator script={selectedScript}/> : <Empty/>}
            </Col>
        </Row> : <Row>
            <Col span={24}>
                {renderTable()}
            </Col>
        </Row>}
    </div>
}
Example #28
Source File: StringFuzzer.tsx    From yakit with GNU Affero General Public License v3.0 4 votes vote down vote up
StringFuzzer: React.FC<StringFuzzerProp> = (props) => {
    const [template, setTemplate] = useState("");
    const [loading, setLoading] = useState(false);
    const [random, _] = useState(randomString(20));
    const token = `client-string-fuzzer-${random}`;

    // 高级配置选项
    const [advanced, setAdvanced] = useState(props.advanced ? "advanced" : "ordinary");
    const [buildTemp, setBuildTemp] = useState<string>();
    const [encodeTemp, setEncodeTemp] = useState<string>();

    const getCurrentEncodeTemplate = () => {
        const res = encodeOperators.filter(i => i.name === encodeTemp);
        if (res.length > 0 && res[0]) {
            return res[0];
        }
        return false
    };

    const getCurrentBuildTemplate = () => {
        const res = fuzzOperators.filter(i => i.name === buildTemp);
        if (res.length > 0 && res[0]) {
            return res[0];
        }
        return false
    };

    useEffect(() => {
        const temp = getCurrentEncodeTemplate();
        if (temp && template && !template.includes(`{{${temp.tag}(`)) {
            setEncodeTemp(undefined)
        }

        if (!template) {
            setBuildTemp(undefined)
        }
    }, [template])

    useEffect(() => {
        if (!random) {
            return
        }

        ipcRenderer.on(token, (e, data: { error: any, data: { Results: string[] } }) => {
            if (data.error) {
                failed(((data.error?.details) || data.error?.detail) || "未知错误")
                return
            }
            const {Results} = data.data;
            let m = showDrawer({
                content: <div style={{height: "100%", overflow: "auto"}}>
                    <PageHeader title={"Payload 测试结果"}>

                    </PageHeader>
                    <div style={{height: "80%"}}>
                        <List
                            size={"small"} dataSource={Results}
                            pagination={{
                                pageSize: 15, showTotal: r => {
                                    return <Tag>总量:{r}</Tag>
                                }, size: "small",
                            }} bordered={true}
                            renderItem={e => {
                                return <List.Item>
                                    <Text
                                        copyable={true}
                                        // ellipsis={{tooltip: true}}
                                        // style={{width: 300}}
                                    >{e}</Text>
                                </List.Item>
                            }}>

                        </List>
                    </div>
                </div>,
                width: "35%", mask: true,
            })
            setLoading(false)
        })
        return () => {
            ipcRenderer.removeAllListeners(token)
        }
    }, [random])

    const removeEncodeTemplateTag = () => {
        const res = encodeOperators.filter(i => i.name === encodeTemp);
        if (res.length > 0 && res[0] && template) {
            const item = res[0];
            let newTemp = template.replace(`{{${item.tag}(`, "");
            const index = newTemp.lastIndexOf(")}}");
            if (index >= 0 && newTemp.length >= (index + 3)) {
                newTemp = newTemp.substr(0, index) + newTemp.substr(index + 3)
                setTemplate(newTemp)
            } else {
                warn("移除编码标签失败(标签结构已被破坏,请重构)")
            }
        } else {
            warn("移除编码标签失败: 找不到标签内容(标签类型)")
        }
        setEncodeTemp(undefined)
    };

    const removeBuildTemplateTag = () => {
        const t = getCurrentBuildTemplate()
        if (t && template) {
            if (!t.tag) {
                warn("不支持移除带参数的基础标签");
                setBuildTemp(undefined)
                return
            }
            let newTemp = template.replace(`{{${t.tag}`, "");
            const index = newTemp.lastIndexOf("}}");
            if (index >= 0 && newTemp.length >= (index + 2)) {
                newTemp = newTemp.substr(0, index) + newTemp.substr(index + 2)
                setTemplate(newTemp)
            } else {
                warn("移除基础标签失败(标签结构已被破坏,请重构)")
            }
        } else {
            warn("移除基础标签失败,空 Payload 或找不到标签数据")
        }
        setBuildTemp(undefined)
    }

    const submit = () => {
        if (!template) {
            notification["warning"]({message: "Fuzz模版为空"})
            return
        }

        setLoading(true)
        ipcRenderer.invoke("string-fuzzer", {template, token})
    }

    return <Spin spinning={loading}>
        <PageHeader title={"Fuzzer Tag 调试工具"}>

        </PageHeader>
        <Tabs defaultActiveKey={advanced}>
            <Tabs.TabPane tab={"简易模式"} key={"ordinary"} disabled={props.disableBasicMode}>
                <PageHeader title={<div style={{fontSize: 14}}>
                    简易模式适合复制 Fuzz 过后的 Payload 来查看结果
                </div>}/>
                <Form
                    onSubmitCapture={e => {
                        e.preventDefault()
                        submit()
                    }}
                    labelCol={{span: 7}} wrapperCol={{span: 15}}
                >
                    <Form.Item label={"Fuzz模版"}>
                        <Input placeholder={"{{randstr}}"} value={template}
                               onChange={e => setTemplate(e.target.value)}/>
                    </Form.Item>
                    <Form.Item label={" "} colon={false}>
                        <Space>
                            <Button type={"primary"} htmlType={"submit"}>
                                查看 Fuzz 结果
                            </Button>
                        </Space>
                    </Form.Item>
                </Form>
            </Tabs.TabPane>
            <Tabs.TabPane key={"advanced"} tab={"调试模式"}>
                <PageHeader title={"调试模式"} subTitle={"调试模式适合生成或者修改 Payload,在调试完成后,可以在 Web Fuzzer 中使用"}/>
                <Space direction={"vertical"} style={{width: "100%"}} size={24}>
                    <div style={{height: 120}}>
                        <YakEditor type={"http"}
                                   value={template} readOnly={false} setValue={setTemplate}/>
                    </div>
                    <Form layout={"horizontal"} onSubmitCapture={e => {
                        e.preventDefault()
                        submit()
                    }} labelCol={{span: 7}} wrapperCol={{span: 14}}>
                        <ManySelectOne
                            label={`选择基础 Fuzz 标签`}
                            value={buildTemp}
                            data={fuzzOperators.map(i => {
                                return {value: i.name, text: i.name}
                            })}
                            setValue={r => {
                                setBuildTemp(r)
                                setEncodeTemp(undefined)
                            }}
                        >

                        </ManySelectOne>
                        {buildTemp && <Form.Item label={" "} colon={false}>
                            <Card bordered={true} title={"基础标签"} size={"small"} extra={[
                                <Button
                                    danger={true}
                                    onClick={() => {
                                        removeBuildTemplateTag()
                                    }}
                                >移除编码标签</Button>
                            ]}>
                                {(() => {
                                    if (!buildTemp) {
                                        return
                                    }
                                    const res = fuzzOperators.filter(i => i.name === buildTemp);
                                    if (res.length > 0 && res[0].optionsRender) {
                                        return res[0].optionsRender(template, setTemplate)
                                    }
                                })()}
                            </Card>
                        </Form.Item>}
                        <ManySelectOne
                            disabled={!!encodeTemp}
                            label={"Payload 编码 / 编码标签"}
                            value={encodeTemp}
                            setValue={e => {
                                setEncodeTemp(e)
                            }}
                            data={encodeOperators.map(i => {
                                return {value: i.name, text: i.name}
                            })}
                        >

                        </ManySelectOne>
                        {encodeTemp && <Form.Item
                            label={" "} colon={false}
                            help={"本标签一般负责对 Payload 进行编码等处理,可以嵌套在一个普通(基础)标签外部"}
                        >
                            <Card bordered={true} title={"编码标签"} size={"small"} extra={[
                                <Button
                                    danger={true}
                                    onClick={() => {
                                        removeEncodeTemplateTag()
                                    }}
                                >移除编码标签</Button>
                            ]}>
                                {(() => {
                                    if (!encodeTemp) {
                                        return
                                    }

                                    const res = encodeOperators.filter(i => i.name === encodeTemp);
                                    if (res.length > 0 && res[0].optionsRender) {
                                        return res[0].optionsRender(template, setTemplate)
                                    }
                                })()}
                            </Card>
                        </Form.Item>}
                        <Form.Item label={" "} colon={false}>
                            <Space>
                                <Button htmlType={"submit"}>查看生成后的 Payload</Button>
                                {props.insertCallback && <Button
                                    type={"primary"}
                                    onClick={() => {
                                        if (props.insertCallback) {
                                            props.insertCallback(template)

                                        }
                                    }}
                                >插入标签所在位置</Button>}
                                <Popconfirm
                                    title={"确认要重置你的 Payload 吗?"}
                                    onConfirm={() => {
                                        setBuildTemp("")
                                        setEncodeTemp("")
                                        setTemplate("")
                                    }}
                                >
                                    <Button>重置</Button>
                                </Popconfirm>
                            </Space>
                        </Form.Item>
                    </Form>
                </Space>
            </Tabs.TabPane>
        </Tabs>
    </Spin>
}
Example #29
Source File: CodecPage.tsx    From yakit with GNU Affero General Public License v3.0 4 votes vote down vote up
CodecPage: React.FC<CodecPageProp> = (props) => {
    const [text, setText] = useState("")
    const [result, setResult] = useState("")
    const [loading, setLoading] = useState(true)

    const [leftWidth, setLeftWidth] = useState<boolean>(false)
    const [rightWidth, setRightWidth] = useState<boolean>(false)
    const [leftLine, setLeftLine] = useState<boolean>(true)
    const [rightLine, setRightLine] = useState<boolean>(false)

    const [codecType, setCodecType] = useState<CodecType>();
    const [params, setParams] = useState<YakExecutorParam[]>([]);
    const [codecPlugin, setCodecPlugin] = useState<CodecType[]>([]);
    const [pluginLoading, setPluginLoading] = useState<boolean>(false)
    const [pluginVisible, setPluginVisible] = useState<boolean>(false)
    let timer: any = null

    const codec = (t: string, params?: YakExecutorParam[], isYakScript?: boolean) => {
        if (!t) {
            failed("BUG: 空的解码类型")
            return
        }
        if (!text && !isYakScript) {
            failed("左侧编辑器内容为空,请输入内容后重试!")
            return
        }

        ipcRenderer
            .invoke("Codec", {Type: t, Text: text, Params: params || [], ScriptName: isYakScript ? t : ""})
            .then((res) => {
                onHandledResult(res?.Result || "")
            })
            .catch((err) => {
                onHandleError(`${err}`)
            })
    }

    const onHandledResult = (data: string) => {
        setResult(data)
    }
    const onHandleError = (err: string) => {
        if (err) failed(`CODEC 解码失败:${err}`)
    }

    useEffect(() => {
        setLoading(true)
        setTimeout(() => setLoading(false), 300)
    }, [])

    const renderCodecTypes = useMemoizedFn((items: CodecType[], notAutoExec?: boolean, isYakScript?: boolean) => {
        return (
            items.map((item) => {
                if ((item.subTypes || []).length > 0) {
                    return (
                        <Dropdown
                            key={item.verbose}
                            overlay={
                                <Menu activeKey={codecType?.key}>
                                    {item.subTypes?.map((subItem) => {
                                        return (
                                            <Menu.Item
                                                key={`${subItem.key}`}
                                                onClick={() => {
                                                    setCodecType(subItem)
                                                    if (!notAutoExec) {
                                                        codec(subItem.key || "", [], isYakScript)
                                                    }
                                                }}>
                                                    <span>
                                                        {subItem.verbose}
                                                    </span>
                                            </Menu.Item>
                                        )
                                    })}
                                </Menu>
                            }
                            placement='bottomLeft'
                        >
                            <Button
                                type={((item?.subTypes || []).filter(i => {
                                    return i.key === codecType?.key
                                })).length > 0 ? "primary" : undefined}
                            >
                                {item.verbose}
                                <DownOutlined/>
                            </Button>
                        </Dropdown>
                    )
                } else {
                    return (
                        <Button
                            key={item.key}
                            type={codecType?.key === item.key ? "primary" : undefined}
                            onClick={() => {
                                setCodecType(item);
                                if (!notAutoExec) {
                                    codec(item.key || "", [], isYakScript)
                                }
                            }}
                            style={{marginRight: 8}}
                        >
                            {item.verbose}
                        </Button>
                    )
                }
            })
        )
    })

    const search = useMemoizedFn((keyword?: string) => {
        setPluginLoading(true)
        queryYakScriptList(
            "codec",
            (i: YakScript[], total) => {
                setCodecPlugin([{
                    subTypes: i.map(script => {
                        return {
                            key: script.ScriptName,
                            help: script.Help,
                            verbose: script.ScriptName,
                            isYakScript: true
                        }
                    }), key: "from-yakit-codec-plugin", verbose: "CODEC 社区插件"
                }])
            },
            () => setTimeout(() => setPluginLoading(false), 300),
            10,
            undefined,
            keyword
        )
    })

    useEffect(() => {
        search()
    }, [])

    return (
        <AutoSpin spinning={loading}>
            <PageHeader
                title={"Codec"} className={"codec-pageheader-title"}
                subTitle={<>
                    {codecType && <Tag color={"geekblue"}>当前类型:{codecType?.verbose}</Tag>}
                    {codecType && (codecType?.params || []).length <= 0 &&
                    <Button type={"primary"} size={"small"} onClick={e => {
                        codec(codecType?.key || "", [], codecType?.isYakScript)
                    }}>立即执行</Button>}
                </>}
            />
            <div className={"codec-function-bar"}>
                <Space direction={"vertical"} style={{width: "100%"}}>
                    <Space>
                        {renderCodecTypes(CodecMenu)}
                    </Space>
                    <Space>
                        {renderCodecTypes(EncAmpDecMenu, true)}
                        {/* {renderCodecTypes(codecPlugin, false, true)} */}
                        <Popover
                            overlayClassName="codec-plugin-lib"
                            trigger="hover"
                            placement="bottomLeft"
                            visible={pluginVisible}
                            onVisibleChange={setPluginVisible}
                            content={
                                <div style={{width: 250}}>
                                    <Input placeholder="模糊搜索插件名" allowClear onChange={event => {
                                        if (timer) {
                                            clearTimeout(timer)
                                            timer = null
                                        }
                                        timer = setTimeout(() => {
                                            search(event.target.value)
                                        }, 500);
                                    }}></Input>
                                    <List
                                        loading={pluginLoading}
                                        size="small"
                                        dataSource={codecPlugin[0]?.subTypes || []}
                                        rowKey={row => row.key || ""}
                                        renderItem={item => <List.Item>
                                            <div style={{width: "100%", padding: "5px 7px"}} onClick={() => {
                                                setCodecType(item)
                                                codec(item.key || "", [], true)
                                                setPluginVisible(false)
                                            }}>
                                                {item.key || ""}
                                            </div>
                                        </List.Item>}
                                    />
                                </div>
                            }>
                            <Button
                                type={(codecPlugin[0]?.subTypes || []).filter(item => codecType?.key === item.key).length !== 0 ? 'primary' : 'default'}>CODEC
                                社区插件 <DownOutlined style={{fontSize: 10}}/></Button>
                        </Popover>
                    </Space>
                    {codecType && codecType?.params && codecType.params.length > 0 && <Row
                        style={{width: "100%"}}
                        gutter={20}
                    >
                        <Col span={codecType?.help ? 18 : 24}>
                            <Divider>设置参数</Divider>
                            <YakScriptParamsSetter
                                primaryParamsOnly={true} styleSize={"small"}
                                Params={(codecType?.params || [])}
                                onParamsConfirm={finalParams => {
                                    setParams([...finalParams])
                                    codec(codecType?.key || "", finalParams, codecType?.isYakScript)
                                }}
                                hideClearButton={true}
                                submitVerbose={"执行"}
                            />
                        </Col>
                        {codecType?.help && <Col span={6} style={{paddingTop: 30}}>
                            <Alert type={"info"} message={codecType?.help}/>
                        </Col>}
                    </Row>}
                </Space>
            </div>
            <div className={"codec-content"}>
                <Row wrap={false} justify='space-between' style={{flexGrow: 1}}>
                    <Col flex={leftWidth ? "0 1 80%" : rightWidth ? "0 1 18%" : "0 1 49%"}>
                        <AutoCard
                            className='codec-card-body'
                            headStyle={{height: 28, minHeight: 28, padding: 0}}
                            bodyStyle={{padding: 0}}
                            extra={
                                <>
                                    <Button
                                        size={"small"}
                                        type={leftLine ? "primary" : "link"}
                                        icon={<LineConversionIcon/>}
                                        onClick={() => setLeftLine(!leftLine)}
                                    />
                                    <Button
                                        size={"small"}
                                        type={leftWidth ? "primary" : "link"}
                                        icon={<ArrowsAltOutlined/>}
                                        onClick={() => {
                                            setLeftWidth(!leftWidth)
                                            setRightWidth(false)
                                        }}
                                    />
                                </>
                            }
                        >
                            <div className='editor-body'>
                                <YakEditor value={text} noWordWrap={!leftLine} setValue={setText}/>
                            </div>
                        </AutoCard>
                    </Col>
                    <Col flex='0 1 2%'>
                        <div className={"exchange-btn"}>
                            <SwapOutlined
                                className={"exchange-icon"}
                                onClick={() => {
                                    const left = text
                                    const right = result
                                    setText(right)
                                    setResult(left)
                                }}
                            />
                        </div>
                    </Col>
                    <Col flex={rightWidth ? "0 1 80%" : leftWidth ? "0 1 18%" : "0 1 49%"}>
                        <AutoCard
                            className='codec-card-body'
                            headStyle={{height: 28, minHeight: 28, padding: 0}}
                            bodyStyle={{padding: 0}}
                            extra={
                                <>
                                    <Button
                                        size={"small"}
                                        type={rightLine ? "primary" : "link"}
                                        icon={<LineConversionIcon/>}
                                        onClick={() => setRightLine(!rightLine)}
                                    />
                                    <Button
                                        size={"small"}
                                        type={rightWidth ? "primary" : "link"}
                                        icon={<ArrowsAltOutlined/>}
                                        onClick={() => {
                                            setRightWidth(!rightWidth)
                                            setLeftWidth(false)
                                        }}
                                    />
                                </>
                            }
                        >
                            <div className='editor-body'>
                                <YakEditor value={result} noWordWrap={!rightLine} setValue={setResult} readOnly={true}
                                           type={"http"}/>
                            </div>
                        </AutoCard>
                    </Col>
                </Row>
            </div>
        </AutoSpin>
    )
}