@ant-design/icons#PoweroffOutlined TypeScript Examples
The following examples show how to use
@ant-design/icons#PoweroffOutlined.
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: Icon.tsx From html2sketch with MIT License | 4 votes |
IconSymbol: FC = () => {
return (
<Row>
{/*<CaretUpOutlined*/}
{/* className="icon"*/}
{/* symbolName={'1.General/2.Icons/1.CaretUpOutlined'}*/}
{/*/>*/}
{/* className="icon"*/}
{/* symbolName={'1.General/2.Icons/2.MailOutlined'}*/}
{/*/>*/}
{/*<StepBackwardOutlined*/}
{/* className="icon"*/}
{/* symbolName={'1.General/2.Icons/2.StepBackwardOutlined'}*/}
{/*/>*/}
{/*<StepForwardOutlined*/}
{/* className="icon"*/}
{/* symbolName={'1.General/2.Icons/2.StepBackwardOutlined'}*/}
{/*/>*/}
<StepForwardOutlined />
<ShrinkOutlined />
<ArrowsAltOutlined />
<DownOutlined />
<UpOutlined />
<LeftOutlined />
<RightOutlined />
<CaretUpOutlined />
<CaretDownOutlined />
<CaretLeftOutlined />
<CaretRightOutlined />
<VerticalAlignTopOutlined />
<RollbackOutlined />
<FastBackwardOutlined />
<FastForwardOutlined />
<DoubleRightOutlined />
<DoubleLeftOutlined />
<VerticalLeftOutlined />
<VerticalRightOutlined />
<VerticalAlignMiddleOutlined />
<VerticalAlignBottomOutlined />
<ForwardOutlined />
<BackwardOutlined />
<EnterOutlined />
<RetweetOutlined />
<SwapOutlined />
<SwapLeftOutlined />
<SwapRightOutlined />
<ArrowUpOutlined />
<ArrowDownOutlined />
<ArrowLeftOutlined />
<ArrowRightOutlined />
<LoginOutlined />
<LogoutOutlined />
<MenuFoldOutlined />
<MenuUnfoldOutlined />
<BorderBottomOutlined />
<BorderHorizontalOutlined />
<BorderInnerOutlined />
<BorderOuterOutlined />
<BorderLeftOutlined />
<BorderRightOutlined />
<BorderTopOutlined />
<BorderVerticleOutlined />
<PicCenterOutlined />
<PicLeftOutlined />
<PicRightOutlined />
<RadiusBottomleftOutlined />
<RadiusBottomrightOutlined />
<RadiusUpleftOutlined />
<RadiusUprightOutlined />
<FullscreenOutlined />
<FullscreenExitOutlined />
<QuestionOutlined />
<PauseOutlined />
<MinusOutlined />
<PauseCircleOutlined />
<InfoOutlined />
<CloseOutlined />
<ExclamationOutlined />
<CheckOutlined />
<WarningOutlined />
<IssuesCloseOutlined />
<StopOutlined />
<EditOutlined />
<CopyOutlined />
<ScissorOutlined />
<DeleteOutlined />
<SnippetsOutlined />
<DiffOutlined />
<HighlightOutlined />
<AlignCenterOutlined />
<AlignLeftOutlined />
<AlignRightOutlined />
<BgColorsOutlined />
<BoldOutlined />
<ItalicOutlined />
<UnderlineOutlined />
<StrikethroughOutlined />
<RedoOutlined />
<UndoOutlined />
<ZoomInOutlined />
<ZoomOutOutlined />
<FontColorsOutlined />
<FontSizeOutlined />
<LineHeightOutlined />
<SortAscendingOutlined />
<SortDescendingOutlined />
<DragOutlined />
<OrderedListOutlined />
<UnorderedListOutlined />
<RadiusSettingOutlined />
<ColumnWidthOutlined />
<ColumnHeightOutlined />
<AreaChartOutlined />
<PieChartOutlined />
<BarChartOutlined />
<DotChartOutlined />
<LineChartOutlined />
<RadarChartOutlined />
<HeatMapOutlined />
<FallOutlined />
<RiseOutlined />
<StockOutlined />
<BoxPlotOutlined />
<FundOutlined />
<SlidersOutlined />
<AndroidOutlined />
<AppleOutlined />
<WindowsOutlined />
<IeOutlined />
<ChromeOutlined />
<GithubOutlined />
<AliwangwangOutlined />
<DingdingOutlined />
<WeiboSquareOutlined />
<WeiboCircleOutlined />
<TaobaoCircleOutlined />
<Html5Outlined />
<WeiboOutlined />
<TwitterOutlined />
<WechatOutlined />
<AlipayCircleOutlined />
<TaobaoOutlined />
<SkypeOutlined />
<FacebookOutlined />
<CodepenOutlined />
<CodeSandboxOutlined />
<AmazonOutlined />
<GoogleOutlined />
<AlipayOutlined />
<AntDesignOutlined />
<AntCloudOutlined />
<ZhihuOutlined />
<SlackOutlined />
<SlackSquareOutlined />
<BehanceSquareOutlined />
<DribbbleOutlined />
<DribbbleSquareOutlined />
<InstagramOutlined />
<YuqueOutlined />
<AlibabaOutlined />
<YahooOutlined />
<RedditOutlined />
<SketchOutlined />
<AccountBookOutlined />
<AlertOutlined />
<ApartmentOutlined />
<ApiOutlined />
<QqOutlined />
<MediumWorkmarkOutlined />
<GitlabOutlined />
<MediumOutlined />
<GooglePlusOutlined />
<AppstoreAddOutlined />
<AppstoreOutlined />
<AudioOutlined />
<AudioMutedOutlined />
<AuditOutlined />
<BankOutlined />
<BarcodeOutlined />
<BarsOutlined />
<BellOutlined />
<BlockOutlined />
<BookOutlined />
<BorderOutlined />
<BranchesOutlined />
<BuildOutlined />
<BulbOutlined />
<CalculatorOutlined />
<CalendarOutlined />
<CameraOutlined />
<CarOutlined />
<CarryOutOutlined />
<CiCircleOutlined />
<CiOutlined />
<CloudOutlined />
<ClearOutlined />
<ClusterOutlined />
<CodeOutlined />
<CoffeeOutlined />
<CompassOutlined />
<CompressOutlined />
<ContactsOutlined />
<ContainerOutlined />
<ControlOutlined />
<CopyrightCircleOutlined />
<CopyrightOutlined />
<CreditCardOutlined />
<CrownOutlined />
<CustomerServiceOutlined />
<DashboardOutlined />
<DatabaseOutlined />
<DeleteColumnOutlined />
<DeleteRowOutlined />
<DisconnectOutlined />
<DislikeOutlined />
<DollarCircleOutlined />
<DollarOutlined />
<DownloadOutlined />
<EllipsisOutlined />
<EnvironmentOutlined />
<EuroCircleOutlined />
<EuroOutlined />
<ExceptionOutlined />
<ExpandAltOutlined />
<ExpandOutlined />
<ExperimentOutlined />
<ExportOutlined />
<EyeOutlined />
<FieldBinaryOutlined />
<FieldNumberOutlined />
<FieldStringOutlined />
<DesktopOutlined />
<DingtalkOutlined />
<FileAddOutlined />
<FileDoneOutlined />
<FileExcelOutlined />
<FileExclamationOutlined />
<FileOutlined />
<FileImageOutlined />
<FileJpgOutlined />
<FileMarkdownOutlined />
<FilePdfOutlined />
<FilePptOutlined />
<FileProtectOutlined />
<FileSearchOutlined />
<FileSyncOutlined />
<FileTextOutlined />
<FileUnknownOutlined />
<FileWordOutlined />
<FilterOutlined />
<FireOutlined />
<FlagOutlined />
<FolderAddOutlined />
<FolderOutlined />
<FolderOpenOutlined />
<ForkOutlined />
<FormatPainterOutlined />
<FrownOutlined />
<FunctionOutlined />
<FunnelPlotOutlined />
<GatewayOutlined />
<GifOutlined />
<GiftOutlined />
<GlobalOutlined />
<GoldOutlined />
<GroupOutlined />
<HddOutlined />
<HeartOutlined />
<HistoryOutlined />
<HomeOutlined />
<HourglassOutlined />
<IdcardOutlined />
<ImportOutlined />
<InboxOutlined />
<InsertRowAboveOutlined />
<InsertRowBelowOutlined />
<InsertRowLeftOutlined />
<InsertRowRightOutlined />
<InsuranceOutlined />
<InteractionOutlined />
<KeyOutlined />
<LaptopOutlined />
<LayoutOutlined />
<LikeOutlined />
<LineOutlined />
<LinkOutlined />
<Loading3QuartersOutlined />
<LoadingOutlined />
<LockOutlined />
<MailOutlined />
<ManOutlined />
<MedicineBoxOutlined />
<MehOutlined />
<MenuOutlined />
<MergeCellsOutlined />
<MessageOutlined />
<MobileOutlined />
<MoneyCollectOutlined />
<MonitorOutlined />
<MoreOutlined />
<NodeCollapseOutlined />
<NodeExpandOutlined />
<NodeIndexOutlined />
<NotificationOutlined />
<NumberOutlined />
<PaperClipOutlined />
<PartitionOutlined />
<PayCircleOutlined />
<PercentageOutlined />
<PhoneOutlined />
<PictureOutlined />
<PoundCircleOutlined />
<PoundOutlined />
<PoweroffOutlined />
<PrinterOutlined />
<ProfileOutlined />
<ProjectOutlined />
<PropertySafetyOutlined />
<PullRequestOutlined />
<PushpinOutlined />
<QrcodeOutlined />
<ReadOutlined />
<ReconciliationOutlined />
<RedEnvelopeOutlined />
<ReloadOutlined />
<RestOutlined />
<RobotOutlined />
<RocketOutlined />
<SafetyCertificateOutlined />
<SafetyOutlined />
<ScanOutlined />
<ScheduleOutlined />
<SearchOutlined />
<SecurityScanOutlined />
<SelectOutlined />
<SendOutlined />
<SettingOutlined />
<ShakeOutlined />
<ShareAltOutlined />
<ShopOutlined />
<ShoppingCartOutlined />
<ShoppingOutlined />
<SisternodeOutlined />
<SkinOutlined />
<SmileOutlined />
<SolutionOutlined />
<SoundOutlined />
<SplitCellsOutlined />
<StarOutlined />
<SubnodeOutlined />
<SyncOutlined />
<TableOutlined />
<TabletOutlined />
<TagOutlined />
<TagsOutlined />
<TeamOutlined />
<ThunderboltOutlined />
<ToTopOutlined />
<ToolOutlined />
<TrademarkCircleOutlined />
<TrademarkOutlined />
<TransactionOutlined />
<TrophyOutlined />
<UngroupOutlined />
<UnlockOutlined />
<UploadOutlined />
<UsbOutlined />
<UserAddOutlined />
<UserDeleteOutlined />
<UserOutlined />
<UserSwitchOutlined />
<UsergroupAddOutlined />
<UsergroupDeleteOutlined />
<VideoCameraOutlined />
<WalletOutlined />
<WifiOutlined />
<BorderlessTableOutlined />
<WomanOutlined />
<BehanceOutlined />
<DropboxOutlined />
<DeploymentUnitOutlined />
<UpCircleOutlined />
<DownCircleOutlined />
<LeftCircleOutlined />
<RightCircleOutlined />
<UpSquareOutlined />
<DownSquareOutlined />
<LeftSquareOutlined />
<RightSquareOutlined />
<PlayCircleOutlined />
<QuestionCircleOutlined />
<PlusCircleOutlined />
<PlusSquareOutlined />
<MinusSquareOutlined />
<MinusCircleOutlined />
<InfoCircleOutlined />
<ExclamationCircleOutlined />
<CloseCircleOutlined />
<CloseSquareOutlined />
<CheckCircleOutlined />
<CheckSquareOutlined />
<ClockCircleOutlined />
<FormOutlined />
<DashOutlined />
<SmallDashOutlined />
<YoutubeOutlined />
<CodepenCircleOutlined />
<AliyunOutlined />
<PlusOutlined />
<LinkedinOutlined />
<AimOutlined />
<BugOutlined />
<CloudDownloadOutlined />
<CloudServerOutlined />
<CloudSyncOutlined />
<CloudUploadOutlined />
<CommentOutlined />
<ConsoleSqlOutlined />
<EyeInvisibleOutlined />
<FileGifOutlined />
<DeliveredProcedureOutlined />
<FieldTimeOutlined />
<FileZipOutlined />
<FolderViewOutlined />
<FundProjectionScreenOutlined />
<FundViewOutlined />
<MacCommandOutlined />
<PlaySquareOutlined />
<OneToOneOutlined />
<RotateLeftOutlined />
<RotateRightOutlined />
<SaveOutlined />
<SwitcherOutlined />
<TranslationOutlined />
<VerifiedOutlined />
<VideoCameraAddOutlined />
<WhatsAppOutlined />
{/*</Col>*/}
</Row>
);
}
Example #2
Source File: MainOperator.tsx From yakit with GNU Affero General Public License v3.0 | 4 votes |
Main: React.FC<MainProp> = (props) => {
const [engineStatus, setEngineStatus] = useState<"ok" | "error">("ok")
const [status, setStatus] = useState<{ addr: string; isTLS: boolean }>()
const [collapsed, setCollapsed] = useState(false)
const [hideMenu, setHideMenu] = useState(false)
const [loading, setLoading] = useState(false)
const [menuItems, setMenuItems] = useState<MenuItemGroup[]>([])
const [routeMenuData, setRouteMenuData] = useState<MenuDataProps[]>(RouteMenuData)
const [notification, setNotification] = useState("")
const [pageCache, setPageCache] = useState<PageCache[]>([
{
verbose: "MITM",
route: Route.HTTPHacker,
singleNode: ContentByRoute(Route.HTTPHacker),
multipleNode: []
}
])
const [currentTabKey, setCurrentTabKey] = useState<string>(Route.HTTPHacker)
// 系统类型
const [system, setSystem] = useState<string>("")
useEffect(() => {
ipcRenderer.invoke('fetch-system-name').then((res) => setSystem(res))
}, [])
// yakit页面关闭是否二次确认提示
const [winCloseFlag, setWinCloseFlag] = useState<boolean>(true)
const [winCloseShow, setWinCloseShow] = useState<boolean>(false)
useEffect(() => {
ipcRenderer
.invoke("get-value", WindowsCloseFlag)
.then((flag: any) => setWinCloseFlag(flag === undefined ? true : flag))
}, [])
// 获取自定义菜单
const updateMenuItems = () => {
setLoading(true)
// Fetch User Defined Plugins
ipcRenderer
.invoke("GetAllMenuItem", {})
.then((data: { Groups: MenuItemGroup[] }) => {
setMenuItems(data.Groups)
})
.catch((e: any) => failed("Update Menu Item Failed"))
.finally(() => setTimeout(() => setLoading(false), 300))
// Fetch Official General Plugins
ipcRenderer
.invoke("QueryYakScript", {
Pagination: genDefaultPagination(1000),
IsGeneralModule: true,
Type: "yak"
} as QueryYakScriptRequest)
.then((data: QueryYakScriptsResponse) => {
const tabList: MenuDataProps[] = cloneDeep(RouteMenuData)
for (let item of tabList) {
if (item.subMenuData) {
if (item.key === Route.GeneralModule) {
const extraMenus: MenuDataProps[] = data.Data.map((i) => {
return {
icon: <EllipsisOutlined/>,
key: `plugin:${i.Id}`,
label: i.ScriptName,
} as unknown as MenuDataProps
})
item.subMenuData.push(...extraMenus)
}
item.subMenuData.sort((a, b) => a.label.localeCompare(b.label))
}
}
setRouteMenuData(tabList)
})
}
useEffect(() => {
updateMenuItems()
ipcRenderer.on("fetch-new-main-menu", (e) => {
updateMenuItems()
})
return () => {
ipcRenderer.removeAllListeners("fetch-new-main-menu")
}
}, [])
useEffect(() => {
if (engineStatus === "error") props.onErrorConfirmed && props.onErrorConfirmed()
}, [engineStatus])
// 整合路由对应名称
const pluginKey = (item: PluginMenuItem) => `plugin:${item.Group}:${item.YakScriptId}`;
const routeKeyToLabel = new Map<string, string>();
routeMenuData.forEach(k => {
(k.subMenuData || []).forEach(subKey => {
routeKeyToLabel.set(`${subKey.key}`, subKey.label)
})
routeKeyToLabel.set(`${k.key}`, k.label)
})
menuItems.forEach((k) => {
k.Items.forEach((value) => {
routeKeyToLabel.set(pluginKey(value), value.Verbose)
})
})
// Tabs Bar Operation Function
const getCacheIndex = (route: string) => {
const targets = pageCache.filter((i) => i.route === route)
return targets.length > 0 ? pageCache.indexOf(targets[0]) : -1
}
const addTabPage = useMemoizedFn(
(route: Route, nodeParams?: { time?: string; node: ReactNode; isRecord?: boolean }) => {
const filterPage = pageCache.filter((i) => i.route === route)
const filterPageLength = filterPage.length
if (singletonRoute.includes(route)) {
if (filterPageLength > 0) {
setCurrentTabKey(route)
} else {
const tabName = routeKeyToLabel.get(route) || `${route}`
setPageCache([
...pageCache,
{
verbose: tabName,
route: route,
singleNode: ContentByRoute(route),
multipleNode: []
}
])
setCurrentTabKey(route)
}
} else {
if (filterPageLength > 0) {
const tabName = routeKeyToLabel.get(route) || `${route}`
const tabId = `${route}-[${randomString(49)}]`
const time = new Date().getTime().toString()
const node: multipleNodeInfo = {
id: tabId,
verbose: `${tabName}-[${filterPage[0].multipleNode.length + 1}]`,
node: nodeParams && nodeParams.node ? nodeParams?.node || <></> : ContentByRoute(route),
time: nodeParams && nodeParams.node ? nodeParams?.time || time : time
}
const pages = pageCache.map((item) => {
if (item.route === route) {
item.multipleNode.push(node)
item.multipleCurrentKey = tabId
return item
}
return item
})
setPageCache([...pages])
setCurrentTabKey(route)
if (nodeParams && !!nodeParams.isRecord) addFuzzerList(nodeParams?.time || time)
} else {
const tabName = routeKeyToLabel.get(route) || `${route}`
const tabId = `${route}-[${randomString(49)}]`
const time = new Date().getTime().toString()
const node: multipleNodeInfo = {
id: tabId,
verbose: `${tabName}-[1]`,
node: nodeParams && nodeParams.node ? nodeParams?.node || <></> : ContentByRoute(route),
time: nodeParams && nodeParams.node ? nodeParams?.time || time : time
}
setPageCache([
...pageCache,
{
verbose: tabName,
route: route,
singleNode: undefined,
multipleNode: [node],
multipleCurrentKey: tabId
}
])
setCurrentTabKey(route)
if (nodeParams && !!nodeParams.isRecord) addFuzzerList(nodeParams?.time || time)
}
}
}
)
const menuAddPage = useMemoizedFn((route: Route) => {
if (route === "ignore") return
if (route === Route.HTTPFuzzer) {
const time = new Date().getTime().toString()
addTabPage(Route.HTTPFuzzer, {
time: time,
node: ContentByRoute(Route.HTTPFuzzer, undefined, {
system: system,
order: time
}),
isRecord: true
})
} else addTabPage(route as Route)
})
const removePage = (route: string) => {
const targetIndex = getCacheIndex(route)
if (targetIndex > 0 && pageCache[targetIndex - 1]) {
const targetCache = pageCache[targetIndex - 1]
setCurrentTabKey(targetCache.route)
}
if (targetIndex === 0 && pageCache[targetIndex + 1]) {
const targetCache = pageCache[targetIndex + 1]
setCurrentTabKey(targetCache.route)
}
if (targetIndex === 0 && pageCache.length === 1) setCurrentTabKey("")
setPageCache(pageCache.filter((i) => i.route !== route))
if (route === Route.HTTPFuzzer) delFuzzerList(1)
}
const updateCacheVerbose = (id: string, verbose: string) => {
const index = getCacheIndex(id)
if (index < 0) return
pageCache[index].verbose = verbose
setPageCache([...pageCache])
}
const setMultipleCurrentKey = useMemoizedFn((key: string, type: Route) => {
const arr = pageCache.map(item => {
if (item.route === type) {
item.multipleCurrentKey = key
return item
}
return item
})
setPageCache([...arr])
})
const removeMultipleNodePage = useMemoizedFn((key: string, type: Route) => {
const removeArr: multipleNodeInfo[] = pageCache.filter(item => item.route === type)[0]?.multipleNode || []
if (removeArr.length === 0) return
const nodes = removeArr.filter(item => item.id === key)
const time = nodes[0].time
let index = 0
for (let i in removeArr) {
if (removeArr[i].id === key) {
index = +i
break
}
}
if (index === 0 && removeArr.length === 1) {
removePage(type)
return
}
let current = ""
let filterArr: multipleNodeInfo[] = []
if (index > 0 && removeArr[index - 1]) {
current = removeArr[index - 1].id
filterArr = removeArr.filter(item => item.id !== key)
}
if (index === 0 && removeArr[index + 1]) {
current = removeArr[index + 1].id
filterArr = removeArr.filter(item => item.id !== key)
}
if (current) {
const arr = pageCache.map(item => {
if (item.route === type) {
item.multipleNode = [...filterArr]
item.multipleCurrentKey = current
return item
}
return item
})
setPageCache([...arr])
if (type === Route.HTTPFuzzer) delFuzzerList(2, time)
}
})
const removeOtherMultipleNodePage = useMemoizedFn((key: string, type: Route) => {
const removeArr: multipleNodeInfo[] = pageCache.filter(item => item.route === type)[0]?.multipleNode || []
if (removeArr.length === 0) return
const nodes = removeArr.filter(item => item.id === key)
const time = nodes[0].time
const arr = pageCache.map(item => {
if (item.route === type) {
item.multipleNode = [...nodes]
item.multipleCurrentKey = key
return item
}
return item
})
setPageCache([...arr])
if (type === Route.HTTPFuzzer) delFuzzerList(3, time)
})
// 全局记录鼠标坐标位置(为右键菜单提供定位)
const coordinateTimer = useRef<any>(null)
useEffect(() => {
document.onmousemove = (e) => {
const {screenX, screenY, clientX, clientY, pageX, pageY} = e
if (coordinateTimer.current) {
clearTimeout(coordinateTimer.current)
coordinateTimer.current = null
}
coordinateTimer.current = setTimeout(() => {
coordinate.screenX = screenX
coordinate.screenY = screenY
coordinate.clientX = clientX
coordinate.clientY = clientY
coordinate.pageX = pageX
coordinate.pageY = pageY
}, 50);
}
}, [])
// 全局注册快捷键功能
const documentKeyDown = useMemoizedFn((e: any) => {
// ctrl + w 关闭tab页面
if (e.code === "KeyW" && (e.ctrlKey || e.metaKey)) {
e.preventDefault()
if (pageCache.length === 0) return
setLoading(true)
removePage(currentTabKey)
setTimeout(() => setLoading(false), 300);
return
}
})
useEffect(() => {
document.onkeydown = documentKeyDown
}, [])
// fuzzer本地缓存
const fuzzerList = useRef<Map<string, fuzzerInfoProp>>(new Map<string, fuzzerInfoProp>())
const saveFuzzerList = debounce(() => {
const historys: fuzzerInfoProp[] = []
fuzzerList.current.forEach((value) => historys.push(value))
historys.sort((a, b) => +a.time - +b.time)
const filters = historys.filter(item => (item.request || "").length < 1000000 && (item.request || "").length > 0)
ipcRenderer.invoke("set-value", FuzzerCache, JSON.stringify(filters.slice(-5)))
}, 500)
const fetchFuzzerList = useMemoizedFn(() => {
setLoading(true)
fuzzerList.current.clear()
ipcRenderer
.invoke("get-value", FuzzerCache)
.then((res: any) => {
const cache = JSON.parse(res || "[]")
for (let item of cache) {
const time = new Date().getTime().toString()
fuzzerList.current.set(time, {...item, time: time})
addTabPage(Route.HTTPFuzzer, {
time: time,
node:
ContentByRoute(
Route.HTTPFuzzer,
undefined,
{
isHttps: item.isHttps || false,
request: item.request || "",
fuzzerParams: item,
system: system,
order: time
}
)
})
}
})
.catch((e) => console.info(e))
.finally(() => setTimeout(() => setLoading(false), 300))
})
const addFuzzerList = (key: string, request?: string, isHttps?: boolean) => {
fuzzerList.current.set(key, {request, isHttps, time: key})
}
const delFuzzerList = (type: number, key?: string) => {
if (type === 1) fuzzerList.current.clear()
if (type === 2 && key) if (fuzzerList.current.has(key)) fuzzerList.current.delete(key)
if (type === 3 && key) {
const info = fuzzerList.current.get(key)
if (info) {
fuzzerList.current.clear()
fuzzerList.current.set(key, info)
}
}
saveFuzzerList()
}
const updateFuzzerList = (key: string, param: fuzzerInfoProp) => {
fuzzerList.current.set(key, param)
saveFuzzerList()
}
useEffect(() => {
ipcRenderer.on("fetch-fuzzer-setting-data", (e, res: any) => updateFuzzerList(res.key, JSON.parse(res.param)))
// 开发环境不展示fuzzer缓存
ipcRenderer.invoke("is-dev").then((flag) => {
if (!flag) fetchFuzzerList()
// fetchFuzzerList()
})
return () => ipcRenderer.removeAllListeners("fetch-fuzzer-setting-data")
}, [])
// 加载补全
useEffect(() => {
ipcRenderer.invoke("GetYakitCompletionRaw").then((data: { RawJson: Uint8Array }) => {
const completionJson = Buffer.from(data.RawJson).toString("utf8")
setCompletions(JSON.parse(completionJson) as CompletionTotal)
// success("加载 Yak 语言自动补全成功 / Load Yak IDE Auto Completion Finished")
})
}, [])
useEffect(() => {
ipcRenderer.invoke("yakit-connect-status").then((data) => {
setStatus(data)
})
ipcRenderer.on("client-engine-status-ok", (e, reason) => {
if (engineStatus !== "ok") setEngineStatus("ok")
})
ipcRenderer.on("client-engine-status-error", (e, reason) => {
if (engineStatus === "ok") setEngineStatus("error")
})
const updateEngineStatus = () => {
ipcRenderer
.invoke("engine-status")
.catch((e: any) => {
setEngineStatus("error")
})
.finally(() => {
})
}
let id = setInterval(updateEngineStatus, 3000)
return () => {
ipcRenderer.removeAllListeners("client-engine-status-error")
ipcRenderer.removeAllListeners("client-engine-status-ok")
clearInterval(id)
}
}, [])
useHotkeys("Ctrl+Alt+T", () => {
})
useEffect(() => {
ipcRenderer.invoke("query-latest-notification").then((e: string) => {
setNotification(e)
if (e) {
success(
<>
<Space direction={"vertical"}>
<span>来自于 yaklang.io 的通知</span>
<Button
type={"link"}
onClick={() => {
showModal({
title: "Notification",
content: (
<>
<MDEditor.Markdown source={e}/>
</>
)
})
}}
>
点击查看
</Button>
</Space>
</>
)
}
})
}, [])
// 新增数据对比页面
useEffect(() => {
ipcRenderer.on("main-container-add-compare", (e, params) => {
const newTabId = `${Route.DataCompare}-[${randomString(49)}]`;
const verboseNameRaw = routeKeyToLabel.get(Route.DataCompare) || `${Route.DataCompare}`;
addTabPage(Route.DataCompare, {node: ContentByRoute(Route.DataCompare, undefined, {system: system})})
// 区分新建对比页面还是别的页面请求对比的情况
ipcRenderer.invoke("created-data-compare")
})
return () => {
ipcRenderer.removeAllListeners("main-container-add-compare")
}
}, [pageCache])
// Global Sending Function(全局发送功能|通过发送新增功能页面)
const addFuzzer = useMemoizedFn((res: any) => {
const {isHttps, request} = res || {}
if (request) {
const time = new Date().getTime().toString()
addTabPage(Route.HTTPFuzzer, {
time: time,
node:
ContentByRoute(
Route.HTTPFuzzer,
undefined,
{
isHttps: isHttps || false,
request: request || "",
system: system,
order: time
}
)
})
addFuzzerList(time, request || "", isHttps || false)
}
})
const addScanPort = useMemoizedFn((res: any) => {
const {URL = ""} = res || {}
if (URL) {
addTabPage(Route.Mod_ScanPort, {
node: ContentByRoute(Route.Mod_ScanPort, undefined, {scanportParams: URL})
})
}
})
const addBrute = useMemoizedFn((res: any) => {
const {URL = ""} = res || {}
if (URL) {
addTabPage(Route.Mod_Brute, {
node: ContentByRoute(Route.Mod_Brute, undefined, {bruteParams: URL})
})
}
})
// 发送到专项漏洞检测modal-show变量
const [bugTestShow, setBugTestShow] = useState<boolean>(false)
const [bugList, setBugList] = useState<BugInfoProps[]>([])
const [bugTestValue, setBugTestValue] = useState<BugInfoProps[]>([])
const [bugUrl, setBugUrl] = useState<string>("")
const addBugTest = useMemoizedFn((type: number, res?: any) => {
const {URL = ""} = res || {}
if (type === 1 && URL) {
setBugUrl(URL)
ipcRenderer.invoke("get-value", CustomBugList)
.then((res: any) => {
setBugList(res ? JSON.parse(res) : [])
setBugTestShow(true)
})
.catch(() => {
})
}
if (type === 2) {
const filter = pageCache.filter(item => item.route === Route.PoC)
if (filter.length === 0) {
addTabPage(Route.PoC)
setTimeout(() => {
ipcRenderer.invoke("send-to-bug-test", {type: bugTestValue, data: bugUrl})
setBugTestValue([])
setBugUrl("")
}, 300);
} else {
ipcRenderer.invoke("send-to-bug-test", {type: bugTestValue, data: bugUrl})
setCurrentTabKey(Route.PoC)
setBugTestValue([])
setBugUrl("")
}
}
})
const addYakRunning = useMemoizedFn((res: any) => {
const {name = "", code = ""} = res || {}
const filter = pageCache.filter(item => item.route === Route.YakScript)
if (!name || !code) return false
if ((filter || []).length === 0) {
addTabPage(Route.YakScript)
setTimeout(() => {
ipcRenderer.invoke("send-to-yak-running", {name, code})
}, 300);
} else {
ipcRenderer.invoke("send-to-yak-running", {name, code})
setCurrentTabKey(Route.YakScript)
}
})
useEffect(() => {
ipcRenderer.on("fetch-send-to-tab", (e, res: any) => {
const {type, data = {}} = res
if (type === "fuzzer") addFuzzer(data)
if (type === "scan-port") addScanPort(data)
if (type === "brute") addBrute(data)
if (type === "bug-test") addBugTest(1, data)
if (type === "plugin-store") addYakRunning(data)
})
return () => {
ipcRenderer.removeAllListeners("fetch-send-to-tab")
}
}, [])
// Tabs Bar 组件
const closeAllCache = useMemoizedFn(() => {
Modal.confirm({
title: "确定要关闭所有 Tabs?",
content: "这样将会关闭所有进行中的进程",
onOk: () => {
delFuzzerList(1)
setPageCache([])
}
})
})
const closeOtherCache = useMemoizedFn((route: string) => {
Modal.confirm({
title: "确定要关闭除此之外所有 Tabs?",
content: "这样将会关闭所有进行中的进程",
onOk: () => {
const arr = pageCache.filter((i) => i.route === route)
setPageCache(arr)
if (route === Route.HTTPFuzzer) delFuzzerList(1)
}
})
})
const bars = (props: any, TabBarDefault: any) => {
return (
<TabBarDefault
{...props}
children={(barNode: React.ReactElement) => {
return (
<DropdownMenu
menu={{
data: [
{key: "all", title: "关闭所有Tabs"},
{key: "other", title: "关闭其他Tabs"}
]
}}
dropdown={{trigger: ["contextMenu"]}}
onClick={(key) => {
switch (key) {
case "all":
closeAllCache()
break
case "other":
closeOtherCache(barNode.key as Route)
break
default:
break
}
}}
>
{barNode}
</DropdownMenu>
)
}}
/>
)
}
return (
<Layout className="yakit-main-layout">
<AutoSpin spinning={loading}>
<Header className="main-laytou-header">
<Row>
<Col span={8}>
<Space>
<div style={{marginLeft: 18, textAlign: "center", height: 60}}>
<Image src={YakLogoBanner} preview={false} width={130} style={{marginTop: 6}}/>
</div>
<Divider type={"vertical"}/>
<YakVersion/>
<YakitVersion/>
{!hideMenu && (
<Button
style={{marginLeft: 4, color: "#207ee8"}}
type={"ghost"}
ghost={true}
onClick={(e) => {
setCollapsed(!collapsed)
}}
icon={collapsed ? <MenuUnfoldOutlined/> : <MenuFoldOutlined/>}
/>
)}
<Button
style={{marginLeft: 4, color: "#207ee8"}}
type={"ghost"}
ghost={true}
onClick={(e) => {
updateMenuItems()
}}
icon={<ReloadOutlined/>}
/>
</Space>
</Col>
<Col span={16} style={{textAlign: "right", paddingRight: 28}}>
<PerformanceDisplay/>
<RiskStatsTag professionalMode={true}/>
<Space>
{/* {status?.isTLS ? <Tag color={"green"}>TLS:通信已加密</Tag> : <Tag color={"red"}>通信未加密</Tag>} */}
{status?.addr && <Tag color={"geekblue"}>{status?.addr}</Tag>}
{/* <Tag color={engineStatus === "ok" ? "green" : "red"}>Yak 引擎状态:{engineStatus}</Tag> */}
<ReversePlatformStatus/>
<Dropdown forceRender={true} overlay={<Menu>
<Menu.Item key={"update"}>
<AutoUpdateYakModuleButton/>
</Menu.Item>
<Menu.Item key={"reverse-global"}>
<ConfigGlobalReverseButton/>
</Menu.Item>
</Menu>} trigger={["click"]}>
<Button icon={<SettingOutlined/>}>
配置
</Button>
</Dropdown>
<Button type={"link"} danger={true} icon={<PoweroffOutlined/>} onClick={() => {
if (winCloseFlag) setWinCloseShow(true)
else {
success("退出当前 Yak 服务器成功")
setEngineStatus("error")
}
}}/>
</Space>
</Col>
</Row>
</Header>
<Content
style={{
margin: 12,
backgroundColor: "#fff",
overflow: "auto"
}}
>
<Layout style={{height: "100%", overflow: "hidden"}}>
{!hideMenu && (
<Sider
style={{backgroundColor: "#fff", overflow: "auto"}}
collapsed={collapsed}
>
<Spin spinning={loading}>
<Space
direction={"vertical"}
style={{
width: "100%"
}}
>
<Menu
theme={"light"}
style={{}}
selectedKeys={[]}
mode={"inline"}
onSelect={(e) => {
if (e.key === "ignore") return
const flag = pageCache.filter(item => item.route === (e.key as Route)).length === 0
if (flag) menuAddPage(e.key as Route)
else setCurrentTabKey(e.key)
}}
>
{menuItems.map((i) => {
if (i.Group === "UserDefined") {
i.Group = "社区插件"
}
return (
<Menu.SubMenu icon={<EllipsisOutlined/>} key={i.Group}
title={i.Group}>
{i.Items.map((item) => {
if (item.YakScriptId > 0) {
return (
<MenuItem icon={<EllipsisOutlined/>}
key={`plugin:${item.Group}:${item.YakScriptId}`}>
<Text
ellipsis={{tooltip: true}}>{item.Verbose}</Text>
</MenuItem>
)
}
return (
<MenuItem icon={<EllipsisOutlined/>}
key={`batch:${item.Group}:${item.Verbose}:${item.MenuItemId}`}>
<Text
ellipsis={{tooltip: true}}>{item.Verbose}</Text>
</MenuItem>
)
})}
</Menu.SubMenu>
)
})}
{(routeMenuData || []).map((i) => {
if (i.subMenuData) {
return (
<Menu.SubMenu icon={i.icon} key={i.key} title={i.label}>
{(i.subMenuData || []).map((subMenu) => {
return (
<MenuItem icon={subMenu.icon} key={subMenu.key}
disabled={subMenu.disabled}>
<Text
ellipsis={{tooltip: true}}>{subMenu.label}</Text>
</MenuItem>
)
})}
</Menu.SubMenu>
)
}
return (
<MenuItem icon={i.icon} key={i.key} disabled={i.disabled}>
{i.label}
</MenuItem>
)
})}
</Menu>
</Space>
</Spin>
</Sider>
)}
<Content style={{
overflow: "hidden",
backgroundColor: "#fff",
marginLeft: 12,
height: "100%",
display: "flex",
flexFlow: "column"
}}>
<div style={{
padding: 12,
paddingTop: 8,
overflow: "hidden",
flex: "1",
display: "flex",
flexFlow: "column"
}}>
{pageCache.length > 0 ? (
<Tabs
style={{display: "flex", flex: "1"}}
tabBarStyle={{marginBottom: 8}}
className='main-content-tabs yakit-layout-tabs'
activeKey={currentTabKey}
onChange={setCurrentTabKey}
size={"small"}
type={"editable-card"}
renderTabBar={(props, TabBarDefault) => {
return bars(props, TabBarDefault)
}}
hideAdd={true}
onTabClick={(key, e) => {
const divExisted = document.getElementById("yakit-cursor-menu")
if (divExisted) {
const div: HTMLDivElement = divExisted as HTMLDivElement
const unmountResult = ReactDOM.unmountComponentAtNode(div)
if (unmountResult && div.parentNode) {
div.parentNode.removeChild(div)
}
}
}}
>
{pageCache.map((i) => {
return (
<Tabs.TabPane
forceRender={true}
key={i.route}
tab={i.verbose}
closeIcon={
<Space>
<Popover
trigger={"click"}
title={"修改名称"}
content={
<>
<Input
size={"small"}
defaultValue={i.verbose}
onBlur={(e) => updateCacheVerbose(i.route, e.target.value)}
/>
</>
}
>
<EditOutlined className='main-container-cion'/>
</Popover>
<CloseOutlined
className='main-container-cion'
onClick={() => removePage(i.route)}
/>
</Space>
}
>
<div
style={{
overflowY: NoScrollRoutes.includes(i.route)
? "hidden"
: "auto",
overflowX: "hidden",
height: "100%",
maxHeight: "100%"
}}
>
{i.singleNode ? (
i.singleNode
) : (
<MainTabs
currentTabKey={currentTabKey}
tabType={i.route}
pages={i.multipleNode}
currentKey={i.multipleCurrentKey || ""}
isShowAdd={true}
setCurrentKey={(key, type) => {
setMultipleCurrentKey(key, type as Route)
}}
removePage={(key, type) => {
removeMultipleNodePage(key, type as Route)
}}
removeOtherPage={(key, type) => {
removeOtherMultipleNodePage(key, type as Route)
}}
onAddTab={() => menuAddPage(i.route)}
></MainTabs>
)}
</div>
</Tabs.TabPane>
)
})}
</Tabs>
) : (
<></>
)}
</div>
</Content>
</Layout>
</Content>
</AutoSpin>
<Modal
visible={winCloseShow}
onCancel={() => setWinCloseShow(false)}
footer={[
<Button key='link' onClick={() => setWinCloseShow(false)}>
取消
</Button>,
<Button key='back' type='primary' onClick={() => {
success("退出当前 Yak 服务器成功")
setEngineStatus("error")
}}>
退出
</Button>
]}
>
<div style={{height: 40}}>
<ExclamationCircleOutlined style={{fontSize: 22, color: "#faad14"}}/>
<span style={{fontSize: 18, marginLeft: 15}}>提示</span>
</div>
<p style={{fontSize: 15, marginLeft: 37}}>是否要退出yakit操作界面,一旦退出,界面内打开内容除fuzzer页外都会销毁</p>
<div style={{marginLeft: 37}}>
<Checkbox
defaultChecked={!winCloseFlag}
value={!winCloseFlag}
onChange={() => {
setWinCloseFlag(!winCloseFlag)
ipcRenderer.invoke("set-value", WindowsCloseFlag, false)
}}
></Checkbox>
<span style={{marginLeft: 8}}>不再出现该提示信息</span>
</div>
</Modal>
<Modal
visible={bugTestShow}
onCancel={() => setBugTestShow(false)}
footer={[
<Button key='link' onClick={() => setBugTestShow(false)}>
取消
</Button>,
<Button key='back' type='primary' onClick={() => {
if ((bugTestValue || []).length === 0) return failed("请选择类型后再次提交")
addBugTest(2)
setBugTestShow(false)
}}>
确定
</Button>
]}
>
<ItemSelects
item={{
label: "专项漏洞类型",
style: {marginTop: 20}
}}
select={{
allowClear: true,
data: BugList.concat(bugList) || [],
optText: "title",
optValue: "key",
value: (bugTestValue || [])[0]?.key,
onChange: (value, option: any) => setBugTestValue(value ? [{
key: option?.key,
title: option?.title
}] : [])
}}
></ItemSelects>
</Modal>
</Layout>
)
}
Example #3
Source File: HackerPlugin.tsx From yakit with GNU Affero General Public License v3.0 | 4 votes |
HackerPlugin: React.FC<HackerPluginProps> = React.memo((props) => {
const [token, setToken] = useState<string>(randomString(40))
const [loading, setLoading] = useState<boolean>(false)
const [lists, setLists, getLists] = useGetState<YakScript[]>([])
const [keyword, setKeyword] = useState<string>("")
const [limit, setLimit] = useState<number>(100)
const [total, setTotal] = useState<number>(0)
const [selected, setSelected] = useState<string[]>([])
const [indeterminate, setIndeterminate] = useState<boolean>(false)
const [checked, setChecked] = useState<boolean>(false)
const containerRef = useRef()
const wrapperRef = useRef()
const [list] = useVirtualList(getLists(), {
containerTarget: containerRef,
wrapperTarget: wrapperRef,
itemHeight: 40,
overscan: 20
})
const [vlistHeigth, setVListHeight] = useState(600)
const [execting, setExecting] = useState<boolean>(false)
const [infoState, {reset, setXtermRef}, xtermRef] = useHoldingIPCRStream(
`execute-packet-yak-script`,
"ExecutePacketYakScript",
token,
() => setExecting(false)
)
const search = useMemoizedFn(() => {
setLoading(true)
queryYakScriptList(
"packet-hack",
(data, total) => {
setTotal(total || 0)
setLists(data)
},
() => setTimeout(() => setLoading(false), 300),
limit,
undefined,
keyword
)
})
const selectYakScript = useMemoizedFn((info: YakScript) => {
setSelected([info.ScriptName])
// if (!selected.includes(info.ScriptName)) {
// setSelected([...selected, info.ScriptName])
// }
})
const unselectYakScript = useMemoizedFn((info: YakScript) => {
setSelected([])
// setSelected(selected.filter((i) => i !== info.ScriptName))
})
// useEffect(() => {
// const totalYakScript = lists.length
// const filterArr = lists.filter((item) => selected.indexOf(item.ScriptName) > -1)
// const IndeterminateFlag =
// (filterArr.length > 0 && filterArr.length < totalYakScript && selected.length !== 0) ||
// (filterArr.length === 0 && selected.length !== 0)
// const checkedFlag = filterArr.length === totalYakScript && selected.length !== 0
// setIndeterminate(IndeterminateFlag)
// setChecked(checkedFlag)
// }, [selected, lists])
const startScript = useMemoizedFn(() => {
if (selected.length === 0) {
failed("请选一个插件后在点击执行")
return
}
setExecting(true)
const params: ExecutePacketYakScriptProp = {
ScriptName: selected[0],
IsHttps: props.isHTTPS,
Request: props.request
}
if (!!props.response) params.Response = props.response
ipcRenderer
.invoke("ExecutePacketYakScript", params, token)
.then(() => {})
.catch((e) => {
failed(`Start Packet Checker Error: ${e}`)
setExecting(false)
})
})
const cancelScript = useMemoizedFn(() => {
ipcRenderer.invoke("cancel-ExecutePacketYakScript", token)
})
useEffect(() => {
search()
}, [])
const renderListItem = useMemoizedFn((info: YakScript) => {
return (
<div key={info.ScriptName} className='list-opt'>
<Checkbox
checked={selected.includes(info.ScriptName)}
onChange={(r) => {
if (r.target.checked) selectYakScript(info)
else unselectYakScript(info)
}}
>
<Space>
<Text style={{maxWidth: 270}} ellipsis={{tooltip: true}}>
{info.ScriptName}
</Text>
{info.Help && (
<Button
size={"small"}
type={"link"}
onClick={() => {
showModal({
width: "40%",
title: "Help",
content: <>{info.Help}</>
})
}}
icon={<QuestionCircleOutlined />}
/>
)}
</Space>
</Checkbox>
<div style={{flex: 1, textAlign: "right"}}>
{info.Author && (
<Tooltip title={info.Author}>
<Button size={"small"} type={"link"} icon={<UserOutlined />} />
</Tooltip>
)}
</div>
</div>
)
})
return (
<div className='mitm-exec-plugin'>
<div className='left-body'>
<AutoCard
size='small'
bordered={false}
title={"数据包扫描插件(暂只支持单选)"}
bodyStyle={{padding: "0 4px", overflowY: "hidden"}}
extra={
<Space>
{/* <Checkbox
indeterminate={indeterminate}
onChange={(r) => {
if (r.target.checked) {
const newSelected = [...lists.map((i) => i.ScriptName), ...selected]
setSelected(newSelected.filter((e, index) => newSelected.indexOf(e) === index))
} else {
setSelected([])
}
}}
checked={checked}
>
全选
</Checkbox> */}
<Popover
title={"额外设置"}
trigger={["click"]}
content={
<div>
<Form
size={"small"}
onSubmitCapture={(e) => {
e.preventDefault()
search()
}}
>
<InputInteger
label={"插件展示数量"}
value={limit}
setValue={setLimit}
formItemStyle={{marginBottom: 4}}
/>
<Form.Item colon={false} label={""} style={{marginBottom: 10}}>
<Button type='primary' htmlType='submit'>
刷新
</Button>
</Form.Item>
</Form>
</div>
}
>
<Button size={"small"} icon={<SettingOutlined />} type={"link"} />
</Popover>
<Popover
title={"搜索插件关键字"}
trigger={["click"]}
content={
<div>
<Form
size={"small"}
onSubmitCapture={(e) => {
e.preventDefault()
search()
}}
>
<InputItem
label={""}
extraFormItemProps={{style: {marginBottom: 4}, colon: false}}
value={keyword}
setValue={setKeyword}
/>
<Form.Item colon={false} label={""} style={{marginBottom: 10}}>
<Button type='primary' htmlType='submit'>
搜索
</Button>
</Form.Item>
</Form>
</div>
}
>
<Button
size={"small"}
type={!!keyword ? "primary" : "link"}
icon={<SearchOutlined />}
/>
</Popover>
{execting ? (
<Button
type='link'
danger
style={{padding: "4px 0"}}
icon={<PoweroffOutlined />}
onClick={cancelScript}
/>
) : (
<Button
type='link'
style={{padding: "4px 0"}}
icon={<CaretRightOutlined />}
onClick={() => {
xtermClear(xtermRef)
reset()
startScript()
}}
/>
)}
</Space>
}
>
<div style={{height: "100%"}}>
<ReactResizeDetector
onResize={(width, height) => {
if (!width || !height) {
return
}
setVListHeight(height)
}}
handleWidth={true}
handleHeight={true}
refreshMode={"debounce"}
refreshRate={50}
/>
<div ref={containerRef as any} style={{height: vlistHeigth, overflow: "auto"}}>
<div ref={wrapperRef as any}>{list.map((i) => renderListItem(i.data))}</div>
</div>
</div>
</AutoCard>
</div>
<div className='right-body'>
<AutoCard
size='small'
bordered={false}
title={
<Space>
{"已选插件 / 当页插件 / 插件总量"}
<Tag>{`${selected.length} / ${lists.length} / ${total}`}</Tag>
</Space>
}
bodyStyle={{padding: 0, paddingLeft: 5}}
>
<PluginResultUI
results={infoState.messageState}
progress={infoState.processState}
featureType={infoState.featureTypeState}
feature={infoState.featureMessageState}
statusCards={infoState.statusState}
loading={loading}
onXtermRef={setXtermRef}
/>
</AutoCard>
</div>
</div>
)
})
Example #4
Source File: YakExecutor.tsx From yakit with GNU Affero General Public License v3.0 | 4 votes |
YakExecutor: React.FC<YakExecutorProp> = (props) => {
const [codePath, setCodePath] = useState<string>("")
const [loading, setLoading] = useState<boolean>(false)
const [fileList, setFileList] = useState<tabCodeProps[]>([])
const [tabList, setTabList] = useState<tabCodeProps[]>([])
const [activeTab, setActiveTab] = useState<string>("")
const [unTitleCount, setUnTitleCount] = useState(1)
const [hintShow, setHintShow] = useState<boolean>(false)
const [hintFile, setHintFile] = useState<string>("")
const [hintIndex, setHintIndex] = useState<number>(0)
const [renameHint, setRenameHint] = useState<boolean>(false)
const [renameIndex, setRenameIndex] = useState<number>(-1)
const [renameFlag, setRenameFlag] = useState<boolean>(false)
const [renameCache, setRenameCache] = useState<string>("")
const [fullScreen, setFullScreen] = useState<boolean>(false)
const [errors, setErrors] = useState<string[]>([])
const [executing, setExecuting] = useState(false)
const [outputEncoding, setOutputEncoding] = useState<"utf8" | "latin1">("utf8")
const xtermAsideRef = useRef(null)
const xtermRef = useRef(null)
const timer = useRef<any>(null)
const [extraParams, setExtraParams] = useState("")
// trigger for updating
const [triggerForUpdatingHistory, setTriggerForUpdatingHistory] = useState<any>(0)
const addFileTab = useMemoizedFn((res: any) => {
const {name, code} = res
const tab: tabCodeProps = {
tab: `${name}.yak`,
code: code,
suffix: "yak",
isFile: false
}
setActiveTab(`${tabList.length}`)
setTabList(tabList.concat([tab]))
setUnTitleCount(unTitleCount + 1)
})
useEffect(() => {
ipcRenderer.on("fetch-send-to-yak-running", (e, res: any) => addFileTab(res))
return () => ipcRenderer.removeAllListeners("fetch-send-to-yak-running")
}, [])
// 自动保存
const autoSave = useMemoizedFn(() => {
for (let tabInfo of tabList) {
if (tabInfo.isFile) {
ipcRenderer.invoke("write-file", {
route: tabInfo.route,
data: tabInfo.code
})
}
}
})
// 保存近期文件内的15个
const saveFiliList = useMemoizedFn(() => {
let files = cloneDeep(fileList).reverse()
files.splice(14)
files = files.reverse()
ipcRenderer.invoke("set-value", RecentFileList, files)
})
// 获取和保存近期打开文件信息,同时展示打开默认内容
useEffect(() => {
let time: any = null
let timer: any = null
setLoading(true)
ipcRenderer
.invoke("get-value", RecentFileList)
.then((value: any) => {
if ((value || []).length !== 0) {
setFileList(value)
} else {
const tab: tabCodeProps = {
tab: `Untitle-${unTitleCount}.yak`,
code: "# input your yak code\nprintln(`Hello Yak World!`)",
suffix: "yak",
isFile: false
}
setActiveTab(`${tabList.length}`)
setTabList([tab])
setUnTitleCount(unTitleCount + 1)
}
})
.catch(() => {})
.finally(() => {
setTimeout(() => setLoading(false), 300)
time = setInterval(() => {
autoSave()
}, 2000)
timer = setInterval(() => {
saveFiliList()
}, 5000)
})
return () => {
saveFiliList()
if (time) clearInterval(time)
if (timer) clearInterval(timer)
}
}, [])
// 全局监听重命名事件是否被打断
useEffect(() => {
document.onmousedown = (e) => {
// @ts-ignore
if (e.path[0].id !== "rename-input" && renameFlag) {
renameCode(renameIndex)
setRenameFlag(false)
}
}
}, [renameFlag])
// 打开文件
const addFile = useMemoizedFn((file: any) => {
const isExists = fileList.filter((item) => item.tab === file.name && item.route === file.path).length === 1
if (isExists) {
for (let index in tabList) {
const item = tabList[index]
if (item.tab === file.name && item.route === file.path) {
setActiveTab(`${index}`)
return false
}
}
}
ipcRenderer
.invoke("fetch-file-content", file.path)
.then((res) => {
const tab: tabCodeProps = {
tab: file.name,
code: res,
suffix: file.name.split(".").pop() === "yak" ? "yak" : "http",
isFile: true,
route: file.path,
extraParams: file.extraParams
}
setActiveTab(`${tabList.length}`)
if (!isExists) setFileList(fileList.concat([tab]))
setTabList(tabList.concat([tab]))
})
.catch(() => {
failed("无法获取该文件内容,请检查后后重试!")
const files = cloneDeep(fileList)
for (let i in files) if (files[i].route === file.path) files.splice(i, 1)
setFileList(files)
})
return false
})
// 新建文件
const newFile = useMemoizedFn(() => {
const tab: tabCodeProps = {
tab: `Untitle-${unTitleCount}.yak`,
code: "# input your yak code\nprintln(`Hello Yak World!`)",
suffix: "yak",
isFile: false
}
setActiveTab(`${tabList.length}`)
setTabList(tabList.concat([tab]))
setUnTitleCount(unTitleCount + 1)
})
//修改文件
const modifyCode = useMemoizedFn((value: string, index: number) => {
const tabs = cloneDeep(tabList)
tabs[index].code = value
setTabList(tabs)
})
// 保存文件
const saveCode = useMemoizedFn((info: tabCodeProps, index: number) => {
if (info.isFile) {
ipcRenderer.invoke("write-file", {
route: info.route,
data: info.code
})
} else {
ipcRenderer.invoke("show-save-dialog", `${codePath}${codePath ? '/' : ''}${info.tab}`).then((res) => {
if (res.canceled) return
const path = res.filePath
const name = res.name
ipcRenderer
.invoke("write-file", {
route: res.filePath,
data: info.code
})
.then(() => {
const suffix = name.split(".").pop()
var tabs = cloneDeep(tabList)
var active = null
tabs = tabs.filter((item) => item.route !== path)
tabs = tabs.map((item, index) => {
if (!item.route && item.tab === info.tab) {
active = index
item.tab = name
item.isFile = true
item.suffix = suffix === "yak" ? suffix : "http"
item.route = path
return item
}
return item
})
if (active !== null) setActiveTab(`${active}`)
setTabList(tabs)
const file: tabCodeProps = {
tab: name,
code: info.code,
isFile: true,
suffix: suffix === "yak" ? suffix : "http",
route: res.filePath,
extraParams: info.extraParams
}
for (let item of fileList) {
if (item.route === file.route) {
return
}
}
setFileList(fileList.concat([file]))
})
})
}
})
//关闭文件
const closeCode = useMemoizedFn((index, isFileList: boolean) => {
const tabInfo = isFileList ? fileList[+index] : tabList[+index]
if (isFileList) {
for (let i in tabList) {
if (tabList[i].tab === tabInfo.tab && tabList[i].route === tabInfo.route) {
const tabs = cloneDeep(tabList)
tabs.splice(i, 1)
setTabList(tabs)
setActiveTab(tabs.length >= 1 ? `0` : "")
}
}
const files = cloneDeep(fileList)
files.splice(+index, 1)
setFileList(files)
} else {
setActiveTab(index)
if (!tabInfo.isFile) {
setHintFile(tabInfo.tab)
setHintIndex(index)
setHintShow(true)
} else {
const tabs = cloneDeep(tabList)
tabs.splice(+index, 1)
setTabList(tabs)
setActiveTab(tabs.length >= 1 ? `0` : "")
}
}
})
// 关闭虚拟文件不保存
const ownCloseCode = useMemoizedFn(() => {
const tabs = cloneDeep(tabList)
tabs.splice(hintIndex, 1)
setTabList(tabs)
setHintShow(false)
setActiveTab(tabs.length >= 1 ? `0` : "")
})
// 删除文件
const delCode = useMemoizedFn((index) => {
const fileInfo = fileList[index]
ipcRenderer
.invoke("delelte-code-file", fileInfo.route)
.then(() => {
for (let i in tabList) {
if (tabList[i].tab === fileInfo.tab && tabList[i].route === fileInfo.route) {
const tabs = cloneDeep(tabList)
tabs.splice(i, 1)
setTabList(tabs)
setActiveTab(tabs.length >= 1 ? `0` : "")
}
}
const arr = cloneDeep(fileList)
arr.splice(index === undefined ? hintIndex : index, 1)
setFileList(arr)
})
.catch(() => {
failed("文件删除失败!")
})
})
//重命名操作
const renameCode = useMemoizedFn((index: number) => {
const tabInfo = fileList[index]
if (renameCache === tabInfo.tab) return
if (!renameCache) return
if (!tabInfo.route) return
const flagStr = tabInfo.route?.indexOf("/") > -1 ? "/" : "\\"
const routes = tabInfo.route?.split(flagStr)
routes?.pop()
ipcRenderer
.invoke("is-exists-file", routes?.concat([renameCache]).join(flagStr))
.then(() => {
const newRoute = routes?.concat([renameCache]).join(flagStr)
if (!tabInfo.route || !newRoute) return
renameFile(index, renameCache, tabInfo.route, newRoute)
})
.catch(() => {
setRenameHint(true)
})
})
// 重命名文件
const renameFile = useMemoizedFn(
(index: number, rename: string, oldRoute: string, newRoute: string, callback?: () => void) => {
ipcRenderer.invoke("rename-file", {old: oldRoute, new: newRoute}).then(() => {
const suffix = rename.split(".").pop()
var files = cloneDeep(fileList)
var tabs = cloneDeep(tabList)
var active = null
files = files.filter((item) => item.route !== newRoute)
tabs = tabs.filter((item) => item.route !== newRoute)
files = files.map((item) => {
if (item.route === oldRoute) {
item.tab = rename
item.suffix = suffix === "yak" ? suffix : "http"
item.route = newRoute
return item
}
return item
})
tabs = tabs.map((item, index) => {
if (item.route === oldRoute) {
active = index
item.tab = rename
item.suffix = suffix === "yak" ? suffix : "http"
item.route = newRoute
return item
}
return item
})
if (active !== null) setActiveTab(`${active}`)
setFileList(files)
setTabList(tabs)
if (callback) callback()
})
}
)
const fileFunction = (kind: string, index: string, isFileList: boolean) => {
const tabCodeInfo = isFileList ? fileList[index] : tabList[index]
switch (kind) {
case "own":
closeCode(index, isFileList)
return
case "other":
const tabInfo: tabCodeProps = cloneDeep(tabList[index])
for (let i in tabList) {
if (i !== index && !tabList[i].isFile) {
const arr: tabCodeProps[] =
+i > +index
? [tabInfo].concat(tabList.splice(+i, tabList.length))
: tabList.splice(+i, tabList.length)
const num = +i > +index ? 1 : 0
setActiveTab(`${num}`)
setTabList(arr)
setHintFile(arr[num].tab)
setHintIndex(num)
setHintShow(true)
return
}
}
const code = cloneDeep(tabList[index])
setTabList([code])
setActiveTab(`0`)
return
case "all":
for (let i in tabList) {
if (!tabList[i].isFile) {
const arr = tabList.splice(+i, tabList.length)
setActiveTab("0")
setTabList(arr)
setHintFile(arr[0].tab)
setHintIndex(0)
setHintShow(true)
return
}
}
setActiveTab("")
setTabList([])
return
case "remove":
closeCode(index, isFileList)
return
case "delete":
delCode(index)
return
case "rename":
setRenameIndex(+index)
setRenameFlag(true)
setRenameCache(tabCodeInfo.tab)
return
}
}
const openFileLayout = (file: any) => {
addFile(file)
}
useEffect(() => {
ipcRenderer.invoke("fetch-code-path")
.then((path: string) => {
ipcRenderer.invoke("is-exists-file", path)
.then(() => {
setCodePath("")
})
.catch(() => {
setCodePath(path)
})
})
}, [])
useEffect(() => {
if (tabList.length === 0) setFullScreen(false)
}, [tabList])
useEffect(() => {
if (!xtermRef) {
return
}
// let buffer = "";
ipcRenderer.on("client-yak-error", async (e: any, data) => {
failed(`FoundError: ${JSON.stringify(data)}`)
if (typeof data === "object") {
setErrors([...errors, `${JSON.stringify(data)}`])
} else if (typeof data === "string") {
setErrors([...errors, data])
} else {
setErrors([...errors, `${data}`])
}
})
ipcRenderer.on("client-yak-end", () => {
info("Yak 代码执行完毕")
setTriggerForUpdatingHistory(getRandomInt(100000))
setTimeout(() => {
setExecuting(false)
}, 300)
})
ipcRenderer.on("client-yak-data", async (e: any, data: ExecResult) => {
if (data.IsMessage) {
// alert(Buffer.from(data.Message).toString("utf8"))
}
if (data?.Raw) {
writeExecResultXTerm(xtermRef, data, outputEncoding)
// writeXTerm(xtermRef, Buffer.from(data.Raw).toString(outputEncoding).replaceAll("\n", "\r\n"))
// monacoEditorWrite(currentOutputEditor, )
}
})
return () => {
ipcRenderer.removeAllListeners("client-yak-data")
ipcRenderer.removeAllListeners("client-yak-end")
ipcRenderer.removeAllListeners("client-yak-error")
}
}, [xtermRef])
const bars = (props: any, TabBarDefault: any) => {
return (
<TabBarDefault
{...props}
children={(barNode: React.ReactElement) => {
return (
<Dropdown
overlay={CustomMenu(barNode.key, false, tabMenu, fileFunction)}
trigger={["contextMenu"]}
>
{barNode}
</Dropdown>
)
}}
/>
)
}
return (
<AutoCard
className={"yak-executor-body"}
// title={"Yak Runner"}
headStyle={{minHeight: 0}}
bodyStyle={{padding: 0, overflow: "hidden"}}
>
<div
style={{width: "100%", height: "100%", display: "flex", backgroundColor: "#E8E9E8"}}
tabIndex={0}
onKeyDown={(e) => {
if (e.keyCode === 78 && (e.ctrlKey || e.metaKey)) {
newFile()
}
if (e.keyCode === 83 && (e.ctrlKey || e.metaKey) && activeTab) {
saveCode(tabList[+activeTab], +activeTab)
}
}}
>
<div style={{width: `${fullScreen ? 0 : 15}%`}}>
<AutoSpin spinning={loading}>
<ExecutorFileList
lists={fileList}
activeFile={tabList[+activeTab]?.route || ""}
renameFlag={renameFlag}
renameIndex={renameIndex}
renameCache={renameCache}
setRenameCache={setRenameCache}
addFile={addFile}
newFile={newFile}
openFile={openFileLayout}
fileFunction={fileFunction}
/>
</AutoSpin>
</div>
<div style={{width: `${fullScreen ? 100 : 85}%`}} className='executor-right-body'>
{tabList.length > 0 && (
<ResizeBox
isVer
firstNode={
<Tabs
className={"right-editor"}
style={{height: "100%"}}
type='editable-card'
activeKey={activeTab}
hideAdd={true}
onChange={(activeTab) => setActiveTab(activeTab)}
onEdit={(key, event: "add" | "remove") => {
switch (event) {
case "remove":
closeCode(key, false)
return
case "add":
return
}
}}
renderTabBar={(props, TabBarDefault) => {
return bars(props, TabBarDefault)
}}
tabBarExtraContent={
tabList.length && (
<Space style={{marginRight: 5}} size={0}>
<Button
style={{height: 25}}
type={"link"}
size={"small"}
disabled={
tabList[+activeTab] && tabList[+activeTab].suffix !== "yak"
}
onClick={(e) => {
let m = showDrawer({
width: "60%",
placement: "left",
title: "选择你的 Yak 模块执行特定功能",
content: (
<>
<YakScriptManagerPage
type={"yak"}
onLoadYakScript={(s) => {
const tab: tabCodeProps = {
tab: `Untitle-${unTitleCount}.yak`,
code: s.Content,
suffix: "yak",
isFile: false
}
info(`加载 Yak 模块:${s.ScriptName}`)
xtermClear(xtermRef)
setActiveTab(`${tabList.length}`)
setTabList(tabList.concat([tab]))
setUnTitleCount(unTitleCount + 1)
m.destroy()
}}
/>
</>
)
})
}}
>
Yak脚本模板
</Button>
<Button
icon={
fullScreen ? (
<FullscreenExitOutlined style={{fontSize: 15}} />
) : (
<FullscreenOutlined style={{fontSize: 15}} />
)
}
type={"link"}
size={"small"}
style={{width: 30, height: 25}}
onClick={() => setFullScreen(!fullScreen)}
/>
<Popover
trigger={["click"]}
title={"设置命令行额外参数"}
placement="bottomRight"
content={
<Space style={{width: 400}}>
<div>yak {tabList[+activeTab]?.tab || "[file]"}</div>
<Divider type={"vertical"} />
<Paragraph
style={{width: 200, marginBottom: 0}}
editable={{
icon: <Space>
<EditOutlined />
<SaveOutlined onClick={(e) => {
e.stopPropagation()
tabList[+activeTab].extraParams = extraParams
setTabList(tabList)
if(tabList[+activeTab].isFile){
const files = fileList.map(item => {
if(item.route === tabList[+activeTab].route){
item.extraParams = extraParams
return item
}
return item
})
setFileList(files)
}
success("保存成功")
}}
/></Space>,
tooltip: '编辑/保存为该文件默认参数',
onChange: setExtraParams
}}
>
{extraParams}
</Paragraph>
</Space>
}
>
<Button type={"link"} icon={<EllipsisOutlined />} onClick={() => {
setExtraParams(tabList[+activeTab]?.extraParams || "")
}} />
</Popover>
{executing ? (
<Button
icon={<PoweroffOutlined style={{fontSize: 15}} />}
type={"link"}
danger={true}
size={"small"}
style={{width: 30, height: 25}}
onClick={() => ipcRenderer.invoke("cancel-yak")}
/>
) : (
<Button
icon={<CaretRightOutlined style={{fontSize: 15}} />}
type={"link"}
ghost={true}
size={"small"}
style={{width: 30, height: 25}}
disabled={
tabList[+activeTab] && tabList[+activeTab].suffix !== "yak"
}
onClick={() => {
setErrors([])
setExecuting(true)
ipcRenderer.invoke("exec-yak", {
Script: tabList[+activeTab].code,
Params: [],
RunnerParamRaw: extraParams
})
}}
/>
)}
</Space>
)
}
>
{tabList.map((item, index) => {
return (
<TabPane tab={item.tab} key={`${index}`}>
<div style={{height: "100%"}}>
<AutoSpin spinning={executing}>
<div style={{height: "100%"}}>
<YakEditor
type={item.suffix}
value={item.code}
setValue={(value) => {
modifyCode(value, index)
}}
/>
</div>
</AutoSpin>
</div>
</TabPane>
)
})}
</Tabs>
}
firstRatio='70%'
secondNode={
<div
ref={xtermAsideRef}
style={{
width: "100%",
height: "100%",
overflow: "hidden",
borderTop: "1px solid #dfdfdf"
}}
>
<Tabs
style={{height: "100%"}}
className={"right-xterm"}
size={"small"}
tabBarExtraContent={
<Space>
<SelectOne
formItemStyle={{marginBottom: 0}}
value={outputEncoding}
setValue={setOutputEncoding}
size={"small"}
data={[
{text: "GBxxx编码", value: "latin1"},
{text: "UTF-8编码", value: "utf8"}
]}
/>
<Button
size={"small"}
icon={<DeleteOutlined />}
type={"link"}
onClick={(e) => {
xtermClear(xtermRef)
}}
/>
</Space>
}
>
<TabPane
tab={<div style={{width: 50, textAlign: "center"}}>输出</div>}
key={"output"}
>
<div style={{width: "100%", height: "100%"}}>
<CVXterm
ref={xtermRef}
options={{
convertEol: true,
theme: {
foreground: "#536870",
background: "#E8E9E8",
cursor: "#536870",
black: "#002831",
brightBlack: "#001e27",
red: "#d11c24",
brightRed: "#bd3613",
green: "#738a05",
brightGreen: "#475b62",
yellow: "#a57706",
brightYellow: "#536870",
blue: "#2176c7",
brightBlue: "#708284",
magenta: "#c61c6f",
brightMagenta: "#5956ba",
cyan: "#259286",
brightCyan: "#819090",
white: "#eae3cb",
brightWhite: "#fcf4dc"
}
}}
/>
</div>
</TabPane>
<TabPane
tab={
<div style={{width: 50, textAlign: "center"}} key={"terminal"}>
终端(监修中)
</div>
}
disabled
>
<Terminal />
</TabPane>
</Tabs>
</div>
}
secondRatio='30%'
/>
)}
{tabList.length === 0 && (
<Empty className='right-empty' description={<p>请点击左侧打开或新建文件</p>}></Empty>
)}
</div>
<Modal
visible={hintShow}
onCancel={() => setHintShow(false)}
footer={[
<Button key='link' onClick={() => setHintShow(false)}>
取消
</Button>,
<Button key='submit' onClick={() => ownCloseCode()}>
不保存
</Button>,
<Button key='back' type='primary' onClick={() => saveCode(tabList[hintIndex], hintIndex)}>
保存
</Button>
]}
>
<div style={{height: 40}}>
<ExclamationCircleOutlined style={{fontSize: 22, color: "#faad14"}} />
<span style={{fontSize: 18, marginLeft: 15}}>文件未保存</span>
</div>
<p style={{fontSize: 15, marginLeft: 37}}>{`是否要保存${hintFile}里面的内容吗?`}</p>
</Modal>
<Modal
visible={renameHint}
onCancel={() => setHintShow(false)}
footer={[
<Button key='link' onClick={() => setRenameHint(false)}>
取消
</Button>,
<Button
key='back'
type='primary'
onClick={() => {
const oldRoute = tabList[renameIndex].route
if (!oldRoute) return
const flagStr = oldRoute?.indexOf("/") > -1 ? "/" : "\\"
const routes = oldRoute?.split(flagStr)
routes?.pop()
const newRoute = routes?.concat([renameCache]).join(flagStr)
if (!oldRoute || !newRoute) return
renameFile(renameIndex, renameCache, oldRoute, newRoute, () => {
setRenameHint(false)
})
}}
>
确定
</Button>
]}
>
<div style={{height: 40}}>
<ExclamationCircleOutlined style={{fontSize: 22, color: "#faad14"}} />
<span style={{fontSize: 18, marginLeft: 15}}>文件已存在</span>
</div>
<p style={{fontSize: 15, marginLeft: 37}}>{`是否要覆盖已存在的文件吗?`}</p>
</Modal>
</div>
</AutoCard>
)
}
Example #5
Source File: MITMPage.tsx From yakit with GNU Affero General Public License v3.0 | 4 votes |
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 #6
Source File: MITMPluginList.tsx From yakit with GNU Affero General Public License v3.0 | 4 votes |
MITMPluginList: React.FC<MITMPluginListProp> = (props) => {
const [initialed, setInitialed] = useState(false);
const [script, setScript] = useState(MITMPluginTemplateShort);
// const [userDefined, setUserDefined] = useState(false);
const [hooks, handlers] = useMap<string, boolean>(new Map<string, boolean>());
const [mode, setMode] = useState<"hot-patch" | "loaded" | "all">("all");
const [refreshTrigger, setRefreshTrigger] = useState(false);
const refresh = useMemoizedFn(() => {
setRefreshTrigger(!refreshTrigger)
})
useEffect(() => {
getValue(MITM_HOTPATCH_CODE).then(e => {
if (!e) {
return
}
setScript(`${e}`)
})
}, [])
// 设置用户模式
const userDefined = mode === "hot-patch";
let hooksItem: { name: string }[] = [];
hooks.forEach((value, key) => {
if (value) {
hooksItem.push({name: key})
}
});
hooksItem = hooksItem.sort((a, b) => a.name.localeCompare(b.name))
useEffect(() => {
// 用于 MITM 的 查看当前 Hooks
ipcRenderer.on("client-mitm-hooks", (e, data: YakScriptHooks[]) => {
const tmp = new Map<string, boolean>()
data.forEach(i => {
i.Hooks.map(hook => {
tmp.set(hook.YakScriptName, true)
})
})
handlers.setAll(tmp)
})
updateHooks()
setTimeout(() => {
setInitialed(true)
}, 300)
return () => {
ipcRenderer.removeAllListeners("client-mitm-hooks");
}
}, []);
return <AutoCard
bordered={false}
bodyStyle={{padding: 0, overflowY: "auto"}}
loading={!initialed}
title={<Space>
<Form size={"small"} onSubmitCapture={e => e.preventDefault()}>
<SelectOne
data={[
{text: "热加载", value: "hot-patch"},
{text: "已启用", value: "loaded"},
{text: "全部", value: "all"},
]}
value={mode}
formItemStyle={{marginBottom: 0}}
setValue={setMode}
/>
</Form>
{mode === "hot-patch" && <Popconfirm
title={"确认重置热加载代码?"}
onConfirm={() => {
setScript(MITMPluginTemplateShort)
refresh()
}}
>
<Button
type={"link"} icon={<ReloadOutlined/>} size={"small"}
/>
</Popconfirm>}
</Space>} size={"small"}
extra={<>
<Space>
{userDefined && <Button
size={"small"} type={"primary"}
onClick={() => {
if (!!script) {
saveValue(MITM_HOTPATCH_CODE, script)
}
props.onSubmitScriptContent && props.onSubmitScriptContent(script)
}}
>加载当前代码</Button>}
{/*: <Button*/}
{/* size={"small"} type={"primary"}*/}
{/* onClick={() => {*/}
{/* enablePlugin()*/}
{/* }}*/}
{/*>加载插件</Button>}*/}
<Button
danger={true}
size={"small"} type={"primary"}
onClick={() => {
props.onExit && props.onExit()
}}
icon={<PoweroffOutlined/>}
>停止</Button>
</Space>
</>}
>
{mode === "hot-patch" && <>
{/* 用户热加载代码 */}
<YakCodeEditor
refreshTrigger={refreshTrigger}
noHeader={true} noPacketModifier={true}
originValue={Buffer.from(script || "")}
onChange={e => setScript(e.toString())}
language={"yak"}
extraEditorProps={{
noMiniMap: true, noWordWrap: true,
} as EditorProps}
/>
</>}
{mode === "all" && <>
<YakModuleList
Type={"mitm,port-scan"}
onClicked={(script: YakScript) => {
}}
Keyword={""}
onYakScriptRender={(i: YakScript, maxWidth?: number) => {
return <MITMYakScriptLoader
script={i} hooks={hooks}
maxWidth={maxWidth}
onSendToPatch={code => {
setScript(code)
setMode("hot-patch")
}}
onSubmitYakScriptId={props.onSubmitYakScriptId}
/>
}}
/>
</>}
{mode === "loaded" && <>
{hooks.size > 0 ? <>
<List pagination={false}>
{hooksItem.map(i => {
return <>
<MITMYakScriptLoader
key={i.name}
onSendToPatch={code => {
setScript(code)
setMode("hot-patch")
}}
script={{ScriptName: i.name} as YakScript} hooks={hooks}
onSubmitYakScriptId={props.onSubmitYakScriptId}
/>
</>
})}
</List>
</> : <>
<Empty description={"未启用 MITM 插件"}/>
</>}
</>}
</AutoCard>
}
Example #7
Source File: App.tsx From pcap2socks-gui with MIT License | 4 votes |
render() {
return (
<Layout className="layout">
<Content className="content-wrapper">
<div className="content">
{(() => {
switch (this.state.stage) {
case STAGE_WELCOME:
return this.renderWelcome();
case STAGE_INTERFACE:
return this.renderInterface();
case STAGE_DEVICE:
return this.renderDevice();
case STAGE_PROXY:
return this.renderProxy();
case STAGE_RUNNING:
return this.renderRunning();
default:
return;
}
})()}
</div>
<div className="footer">
{(() => {
if (this.state.stage > STAGE_WELCOME && this.state.stage <= STAGE_PROXY) {
return (
<Button
className="button"
disabled={this.state.loading > 0}
icon={<LeftOutlined />}
onClick={() => this.setState({ stage: this.state.stage - 1 })}
>
上一步
</Button>
);
}
})()}
{(() => {
if (this.state.stage === STAGE_INTERFACE) {
return (
<Button
className="button"
disabled={this.state.loading > 0 && this.state.loading !== 1}
icon={<ReloadOutlined />}
onClick={this.updateInterfaces}
>
刷新网卡列表
</Button>
);
}
})()}
{(() => {
if (this.state.stage === STAGE_PROXY) {
return (
<Button
className="button"
disabled={this.state.loading > 0}
icon={<FolderOpenOutlined />}
onClick={() => {
const node = document.getElementById("open");
if (node) {
node.click();
}
}}
>
导入代理配置
<input id="open" type="file" onChange={this.import} style={{ display: "none" }} />
</Button>
);
}
})()}
{(() => {
if (this.state.stage === STAGE_PROXY) {
return (
<Button className="button" icon={<ExportOutlined />} onClick={this.export}>
导出代理配置
</Button>
);
}
})()}
{(() => {
if (this.state.stage === STAGE_PROXY) {
return (
<Button
className="button"
disabled={this.state.loading > 0 && this.state.loading !== 2}
loading={this.state.loading === 2}
icon={<ExperimentOutlined />}
onClick={this.test}
>
测试代理服务器
</Button>
);
}
})()}
{(() => {
if (this.state.stage === STAGE_WELCOME && this.state.ready) {
return (
<Tooltip title={this.state.destination}>
<Button
className="button"
type="primary"
disabled={this.state.loading > 0 && this.state.loading !== 3}
loading={this.state.loading === 3}
icon={<PlayCircleOutlined />}
onClick={this.run}
>
以上次的配置运行
</Button>
</Tooltip>
);
}
})()}
{(() => {
if (this.state.stage >= STAGE_WELCOME && this.state.stage < STAGE_PROXY) {
return (
<Button
className="button"
disabled={this.state.loading > 0}
icon={<RightOutlined />}
type="primary"
onClick={() => this.setState({ stage: this.state.stage + 1 })}
>
下一步
</Button>
);
}
})()}
{(() => {
if (this.state.stage === STAGE_PROXY) {
return (
<Button
className="button"
type="primary"
disabled={this.state.loading > 0 && this.state.loading !== 3}
loading={this.state.loading === 3}
icon={<PoweroffOutlined />}
onClick={this.run}
>
运行
</Button>
);
}
})()}
{(() => {
if (this.state.stage === STAGE_RUNNING) {
return (
<Button className="button" icon={<GlobalOutlined />} onClick={this.notifyNetwork}>
显示网络设置
</Button>
);
}
})()}
{(() => {
if (this.state.stage === STAGE_RUNNING) {
return (
<Button
className="button"
type="primary"
danger
disabled={this.state.loading > 0 && this.state.loading !== 4}
loading={this.state.loading === 4}
icon={<PoweroffOutlined />}
onClick={this.stopConfirm}
>
停止
</Button>
);
}
})()}
</div>
</Content>
</Layout>
);
}