mirror of
https://github.com/kubeshark/kubeshark.git
synced 2025-07-31 08:20:25 +00:00
Add Represent
method to the Dissector
interface and manipulate the UI through this method
This commit is contained in:
parent
03099edfc1
commit
c5969c267e
@ -236,9 +236,12 @@ func GetEntry(c *gin.Context) {
|
||||
// "msg": "Can't get entry details",
|
||||
// })
|
||||
// }
|
||||
extension := extensionsMap[entryData.ProtocolName]
|
||||
representation, _ := extension.Dissector.Represent(entryData.Entry)
|
||||
c.JSON(http.StatusOK, tapApi.MizuEntryWrapper{
|
||||
Protocol: extensionsMap[entryData.ProtocolName].Protocol,
|
||||
Data: entryData,
|
||||
Protocol: extension.Protocol,
|
||||
Representation: string(representation),
|
||||
Data: entryData,
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -66,6 +66,7 @@ type Dissector interface {
|
||||
Dissect(b *bufio.Reader, isClient bool, tcpID *TcpID, emitter Emitter)
|
||||
Analyze(item *OutputChannelItem, entryId string, resolvedSource string, resolvedDestination string) *MizuEntry
|
||||
Summarize(entry *MizuEntry) *BaseEntryDetails
|
||||
Represent(entry string) ([]byte, error)
|
||||
}
|
||||
|
||||
type Emitting struct {
|
||||
@ -109,8 +110,9 @@ type MizuEntry struct {
|
||||
}
|
||||
|
||||
type MizuEntryWrapper struct {
|
||||
Protocol Protocol `json:"protocol"`
|
||||
Data MizuEntry `json:"data"`
|
||||
Protocol Protocol `json:"protocol"`
|
||||
Representation string `json:"representation"`
|
||||
Data MizuEntry `json:"data"`
|
||||
}
|
||||
|
||||
type BaseEntryDetails struct {
|
||||
|
@ -47,4 +47,9 @@ func (d dissecting) Summarize(entry *api.MizuEntry) *api.BaseEntryDetails {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d dissecting) Represent(entry string) ([]byte, error) {
|
||||
// TODO: Implement
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
var Dissector dissecting
|
||||
|
@ -17,12 +17,12 @@ var responseCounter uint
|
||||
|
||||
var protocol api.Protocol = api.Protocol{
|
||||
Name: "http",
|
||||
LongName: "Hypertext Transfer Protocol -- HTTP/1.0",
|
||||
LongName: "Hypertext Transfer Protocol -- HTTP/1.1",
|
||||
Abbreviation: "HTTP",
|
||||
BackgroundColor: "#205cf5",
|
||||
ForegroundColor: "#ffffff",
|
||||
FontSize: 10,
|
||||
ReferenceLink: "https://www.ietf.org/rfc/rfc1945.txt",
|
||||
ReferenceLink: "https://datatracker.ietf.org/doc/html/rfc2616",
|
||||
OutboundPorts: []string{"80", "8080", "443"},
|
||||
InboundPorts: []string{},
|
||||
}
|
||||
@ -160,4 +160,134 @@ func (d dissecting) Summarize(entry *api.MizuEntry) *api.BaseEntryDetails {
|
||||
}
|
||||
}
|
||||
|
||||
func representRequest(request map[string]interface{}) []interface{} {
|
||||
repRequest := make([]interface{}, 0)
|
||||
|
||||
details, _ := json.Marshal([]map[string]string{
|
||||
{
|
||||
"name": "Method",
|
||||
"value": request["method"].(string),
|
||||
},
|
||||
{
|
||||
"name": "URL",
|
||||
"value": request["url"].(string),
|
||||
},
|
||||
{
|
||||
"name": "Body Size",
|
||||
"value": fmt.Sprintf("%g bytes", request["bodySize"].(float64)),
|
||||
},
|
||||
})
|
||||
repRequest = append(repRequest, map[string]string{
|
||||
"type": "table",
|
||||
"title": "Details",
|
||||
"data": string(details),
|
||||
})
|
||||
|
||||
headers, _ := json.Marshal(request["headers"].([]interface{}))
|
||||
repRequest = append(repRequest, map[string]string{
|
||||
"type": "table",
|
||||
"title": "Headers",
|
||||
"data": string(headers),
|
||||
})
|
||||
|
||||
cookies, _ := json.Marshal(request["cookies"].([]interface{}))
|
||||
repRequest = append(repRequest, map[string]string{
|
||||
"type": "table",
|
||||
"title": "Cookies",
|
||||
"data": string(cookies),
|
||||
})
|
||||
|
||||
queryString, _ := json.Marshal(request["queryString"].([]interface{}))
|
||||
repRequest = append(repRequest, map[string]string{
|
||||
"type": "table",
|
||||
"title": "Query String",
|
||||
"data": string(queryString),
|
||||
})
|
||||
|
||||
postData, _ := request["postData"].(map[string]interface{})
|
||||
mimeType, _ := postData["mimeType"]
|
||||
if mimeType == nil {
|
||||
mimeType = "N/A"
|
||||
}
|
||||
text, _ := postData["text"]
|
||||
if text != nil {
|
||||
repRequest = append(repRequest, map[string]string{
|
||||
"type": "body",
|
||||
"title": "POST Data",
|
||||
"encoding": "base64", // FIXME: Does `request` have it?
|
||||
"mime_type": mimeType.(string),
|
||||
"data": text.(string),
|
||||
})
|
||||
}
|
||||
|
||||
return repRequest
|
||||
}
|
||||
|
||||
func representResponse(response map[string]interface{}) []interface{} {
|
||||
repResponse := make([]interface{}, 0)
|
||||
|
||||
details, _ := json.Marshal([]map[string]string{
|
||||
{
|
||||
"name": "Status",
|
||||
"value": fmt.Sprintf("%g", response["status"].(float64)),
|
||||
},
|
||||
{
|
||||
"name": "Status Text",
|
||||
"value": response["statusText"].(string),
|
||||
},
|
||||
{
|
||||
"name": "Body Size",
|
||||
"value": fmt.Sprintf("%g bytes", response["bodySize"].(float64)),
|
||||
},
|
||||
})
|
||||
repResponse = append(repResponse, map[string]string{
|
||||
"type": "table",
|
||||
"title": "Details",
|
||||
"data": string(details),
|
||||
})
|
||||
|
||||
headers, _ := json.Marshal(response["headers"].([]interface{}))
|
||||
repResponse = append(repResponse, map[string]string{
|
||||
"type": "table",
|
||||
"title": "Headers",
|
||||
"data": string(headers),
|
||||
})
|
||||
|
||||
cookies, _ := json.Marshal(response["cookies"].([]interface{}))
|
||||
repResponse = append(repResponse, map[string]string{
|
||||
"type": "table",
|
||||
"title": "Cookies",
|
||||
"data": string(cookies),
|
||||
})
|
||||
|
||||
content, _ := response["content"].(map[string]interface{})
|
||||
mimeType, _ := content["mimeType"]
|
||||
encoding, _ := content["encoding"]
|
||||
text, _ := content["text"]
|
||||
if text != nil {
|
||||
repResponse = append(repResponse, map[string]string{
|
||||
"type": "body",
|
||||
"title": "Body",
|
||||
"encoding": encoding.(string),
|
||||
"mime_type": mimeType.(string),
|
||||
"data": text.(string),
|
||||
})
|
||||
}
|
||||
|
||||
return repResponse
|
||||
}
|
||||
|
||||
func (d dissecting) Represent(entry string) ([]byte, error) {
|
||||
var root map[string]interface{}
|
||||
json.Unmarshal([]byte(entry), &root)
|
||||
representation := make(map[string]interface{}, 0)
|
||||
request := root["request"].(map[string]interface{})["payload"].(map[string]interface{})
|
||||
response := root["response"].(map[string]interface{})["payload"].(map[string]interface{})
|
||||
repRequest := representRequest(request)
|
||||
repResponse := representResponse(response)
|
||||
representation["request"] = repRequest
|
||||
representation["response"] = repResponse
|
||||
return json.Marshal(representation)
|
||||
}
|
||||
|
||||
var Dissector dissecting
|
||||
|
@ -47,4 +47,9 @@ func (d dissecting) Summarize(entry *api.MizuEntry) *api.BaseEntryDetails {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d dissecting) Represent(entry string) ([]byte, error) {
|
||||
// TODO: Implement
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
var Dissector dissecting
|
||||
|
@ -77,10 +77,7 @@ export const HAREntryDetailed: React.FC<HarEntryDetailedProps> = ({classes, harE
|
||||
<HarEntryTitle protocol={harEntry.protocol} har={har}/>
|
||||
{har && <HarEntrySummary har={har}/>}
|
||||
<>
|
||||
{har && <HAREntryViewer
|
||||
harObject={har}
|
||||
className={classes?.root ?? styles.har}
|
||||
/>}
|
||||
{har && <HAREntryViewer representation={harEntry.representation}/>}
|
||||
</>
|
||||
</>
|
||||
};
|
||||
|
@ -31,7 +31,6 @@
|
||||
margin: .3rem 0
|
||||
|
||||
.dataKey
|
||||
text-transform: capitalize
|
||||
color: $blue-gray
|
||||
margin: 0 0.5rem 0 0
|
||||
text-align: right
|
||||
|
@ -80,9 +80,9 @@ export const HAREntryBodySection: React.FC<HAREntryBodySectionProps> = ({
|
||||
const bodyBuf = encoding === 'base64' ? atob(chunk) : chunk;
|
||||
|
||||
try {
|
||||
if (jsonLikeFormats.some(format => content?.mimeType?.indexOf(format) > -1)) {
|
||||
if (jsonLikeFormats.some(format => contentType?.indexOf(format) > -1)) {
|
||||
return JSON.stringify(JSON.parse(bodyBuf), null, 2);
|
||||
} else if (protobufFormats.some(format => content?.mimeType?.indexOf(format) > -1)) {
|
||||
} else if (protobufFormats.some(format => contentType?.indexOf(format) > -1)) {
|
||||
// Replace all non printable characters (ASCII)
|
||||
const protobufDecoder = new ProtobufDecoder(bodyBuf, true);
|
||||
return JSON.stringify(protobufDecoder.decode().toSimple(), null, 2);
|
||||
@ -94,17 +94,17 @@ export const HAREntryBodySection: React.FC<HAREntryBodySectionProps> = ({
|
||||
}
|
||||
|
||||
const getLanguage = (mimetype) => {
|
||||
const chunk = content.text?.slice(0, 100);
|
||||
const chunk = content?.slice(0, 100);
|
||||
if (chunk.indexOf('html') > 0 || chunk.indexOf('HTML') > 0) return supportedLanguages[0][1];
|
||||
const language = supportedLanguages.find(el => (mimetype + contentType).indexOf(el[0]) > -1);
|
||||
return language ? language[1] : 'default';
|
||||
}
|
||||
|
||||
return <React.Fragment>
|
||||
{content && content.text?.length > 0 && <HAREntrySectionContainer title='Body'>
|
||||
{content && content?.length > 0 && <HAREntrySectionContainer title='Body'>
|
||||
<table>
|
||||
<tbody>
|
||||
<HAREntryViewLine label={'Mime type'} value={content?.mimeType}/>
|
||||
<HAREntryViewLine label={'Mime type'} value={contentType}/>
|
||||
<HAREntryViewLine label={'Encoding'} value={encoding}/>
|
||||
</tbody>
|
||||
</table>
|
||||
@ -118,7 +118,7 @@ export const HAREntryBodySection: React.FC<HAREntryBodySectionProps> = ({
|
||||
|
||||
<SyntaxHighlighter
|
||||
isWrapped={isWrapped}
|
||||
code={formatTextBody(content.text)}
|
||||
code={formatTextBody(content)}
|
||||
language={content?.mimeType ? getLanguage(content.mimeType) : 'default'}
|
||||
/>
|
||||
</HAREntrySectionContainer>}
|
||||
@ -205,52 +205,47 @@ export const HAREntryTablePolicySection: React.FC<HAREntryPolicySectionProps> =
|
||||
<table>
|
||||
<tbody>
|
||||
{arrayToIterate.map(({rule, matched}, index) => {
|
||||
|
||||
|
||||
return (
|
||||
<HAREntryPolicySectionContainer key={index} label={rule.Name} matched={matched && (rule.Type === 'latency' ? rule.Latency >= latency : true)? "Success" : "Failure"}>
|
||||
{
|
||||
|
||||
<>
|
||||
{
|
||||
rule.Key != "" ?
|
||||
rule.Key !== "" ?
|
||||
<tr className={styles.dataValue}><td><b>Key</b>:</td><td>{rule.Key}</td></tr>
|
||||
: null
|
||||
}
|
||||
{
|
||||
rule.Latency != "" ?
|
||||
rule.Latency !== "" ?
|
||||
<tr className={styles.dataValue}><td><b>Latency:</b></td> <td>{rule.Latency}</td></tr>
|
||||
: null
|
||||
}
|
||||
{
|
||||
rule.Method != "" ?
|
||||
rule.Method !== "" ?
|
||||
<tr className={styles.dataValue}><td><b>Method:</b></td> <td>{rule.Method}</td></tr>
|
||||
: null
|
||||
}
|
||||
{
|
||||
rule.Path != "" ?
|
||||
rule.Path !== "" ?
|
||||
<tr className={styles.dataValue}><td><b>Path:</b></td> <td>{rule.Path}</td></tr>
|
||||
: null
|
||||
}
|
||||
{
|
||||
rule.Service != "" ?
|
||||
rule.Service !== "" ?
|
||||
<tr className={styles.dataValue}><td><b>Service:</b></td> <td>{service}</td></tr>
|
||||
: null
|
||||
}
|
||||
{
|
||||
rule.Type != "" ?
|
||||
rule.Type !== "" ?
|
||||
<tr className={styles.dataValue}><td><b>Type:</b></td> <td>{rule.Type}</td></tr>
|
||||
: null
|
||||
}
|
||||
{
|
||||
rule.Value != "" ?
|
||||
rule.Value !== "" ?
|
||||
<tr className={styles.dataValue}><td><b>Value:</b></td> <td>{rule.Value}</td></tr>
|
||||
: null
|
||||
}
|
||||
</>
|
||||
}
|
||||
|
||||
|
||||
</HAREntryPolicySectionContainer>
|
||||
)
|
||||
}
|
||||
@ -259,8 +254,7 @@ export const HAREntryTablePolicySection: React.FC<HAREntryPolicySectionProps> =
|
||||
</tbody>
|
||||
</table>
|
||||
</HAREntrySectionContainer>
|
||||
|
||||
</> : <span/>
|
||||
}
|
||||
</React.Fragment>
|
||||
}
|
||||
}
|
||||
|
@ -3,11 +3,34 @@ import styles from './HAREntryViewer.module.sass';
|
||||
import Tabs from "../Tabs";
|
||||
import {HAREntryTableSection, HAREntryBodySection, HAREntryTablePolicySection} from "./HAREntrySections";
|
||||
|
||||
const MIME_TYPE_KEY = 'mimeType';
|
||||
const SectionsRepresentation: React.FC<any> = ({data}) => {
|
||||
const sections = []
|
||||
|
||||
const HAREntryDisplay: React.FC<any> = ({har, entry, isCollapsed: initialIsCollapsed, isResponseMocked}) => {
|
||||
const {request, response} = JSON.parse(entry);
|
||||
const rulesMatched = har.log.entries[0].rulesMatched
|
||||
for (const [i, row] of data.entries()) {
|
||||
switch (row.type) {
|
||||
case "table":
|
||||
sections.push(
|
||||
<HAREntryTableSection title={row.title} arrayToIterate={JSON.parse(row.data)}/>
|
||||
)
|
||||
break;
|
||||
case "body":
|
||||
sections.push(
|
||||
<HAREntryBodySection content={row.data} encoding={row.encoding} contentType={row.mime_type}/>
|
||||
)
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return <>{sections}</>;
|
||||
}
|
||||
|
||||
const AutoRepresentation: React.FC<any> = ({representation, isResponseMocked}) => {
|
||||
console.log(representation)
|
||||
const {request, response} = JSON.parse(representation);
|
||||
|
||||
const rulesMatched = []
|
||||
const TABS = [
|
||||
{tab: 'request'},
|
||||
{
|
||||
@ -22,50 +45,33 @@ const HAREntryDisplay: React.FC<any> = ({har, entry, isCollapsed: initialIsColla
|
||||
const [currentTab, setCurrentTab] = useState(TABS[0].tab);
|
||||
|
||||
return <div className={styles.harEntry}>
|
||||
|
||||
{!initialIsCollapsed && <div className={styles.body}>
|
||||
{<div className={styles.body}>
|
||||
<div className={styles.bodyHeader}>
|
||||
<Tabs tabs={TABS} currentTab={currentTab} onChange={setCurrentTab} leftAligned/>
|
||||
{request?.url && <a className={styles.endpointURL} href={request.payload.url} target='_blank' rel="noreferrer">{request.payload.url}</a>}
|
||||
</div>
|
||||
{
|
||||
currentTab === TABS[0].tab && <React.Fragment>
|
||||
<HAREntryTableSection title={'Headers'} arrayToIterate={request.payload.headers}/>
|
||||
|
||||
<HAREntryTableSection title={'Cookies'} arrayToIterate={request.payload.cookies}/>
|
||||
|
||||
{request.payload?.postData && <HAREntryBodySection content={request.payload.postData} encoding={request.payload.postData.comment} contentType={request.payload.postData[MIME_TYPE_KEY]}/>}
|
||||
|
||||
<HAREntryTableSection title={'Query'} arrayToIterate={request.payload.queryString}/>
|
||||
</React.Fragment>
|
||||
}
|
||||
{currentTab === TABS[0].tab && <React.Fragment>
|
||||
<SectionsRepresentation data={request}/>
|
||||
</React.Fragment>}
|
||||
{currentTab === TABS[1].tab && <React.Fragment>
|
||||
<HAREntryTableSection title={'Headers'} arrayToIterate={response.payload.headers}/>
|
||||
|
||||
<HAREntryBodySection content={response.payload.content} encoding={response.payload.content?.encoding} contentType={response.payload.content?.mimeType}/>
|
||||
|
||||
<HAREntryTableSection title={'Cookies'} arrayToIterate={response.payload.cookies}/>
|
||||
<SectionsRepresentation data={response}/>
|
||||
</React.Fragment>}
|
||||
{currentTab === TABS[2].tab && <React.Fragment>
|
||||
<HAREntryTablePolicySection service={har.log.entries[0].service} title={'Rule'} latency={0} response={response} arrayToIterate={rulesMatched ? rulesMatched : []}/>
|
||||
{// FIXME: Fix here
|
||||
<HAREntryTablePolicySection service={representation.log.entries[0].service} title={'Rule'} latency={0} response={response} arrayToIterate={rulesMatched ? rulesMatched : []}/>}
|
||||
</React.Fragment>}
|
||||
</div>}
|
||||
</div>;
|
||||
}
|
||||
|
||||
interface Props {
|
||||
harObject: any;
|
||||
className?: string;
|
||||
representation: any;
|
||||
isResponseMocked?: boolean;
|
||||
showTitle?: boolean;
|
||||
}
|
||||
|
||||
const HAREntryViewer: React.FC<Props> = ({harObject, className, isResponseMocked, showTitle=true}) => {
|
||||
const {log: {entries}} = harObject;
|
||||
const isCollapsed = entries.length > 1;
|
||||
return <div className={`${className ? className : ''}`}>
|
||||
{Object.keys(entries).map((entry: any, index) => <HAREntryDisplay har={harObject} isCollapsed={isCollapsed} key={index} entry={entries[entry].entry} isResponseMocked={isResponseMocked} showTitle={showTitle}/>)}
|
||||
</div>
|
||||
const HAREntryViewer: React.FC<Props> = ({representation, isResponseMocked, showTitle=true}) => {
|
||||
return <AutoRepresentation representation={representation} isResponseMocked={isResponseMocked} showTitle={showTitle}/>
|
||||
};
|
||||
|
||||
export default HAREntryViewer;
|
||||
|
Loading…
Reference in New Issue
Block a user