
استخراج عناوين URL والمسارات والأسرار وغيرها من الأجزاء المثيرة للاهتمام من JavaScript
jsluice هي حزمة Go وأداة سطر أوامر لاستخراج عناوين URL والمسارات والأسرار
والبيانات الأخرى المثيرة للاهتمام من كود JavaScript المصدري.
إذا كنت تريد القيام بهذه الأشياء مباشرة: انظر إلى أداة سطر الأوامر.
إذا كنت تريد دمج إمكانيات jsluice مع مشروعك الخاص: انظر إلى الأمثلة،
واقرأ توثيق الحزمة.
لتثبيت أداة سطر الأوامر، شغّل:
▶ go install github.com/BishopFox/jsluice/cmd/jsluice@latest
لإضافة الحزمة إلى مشروعك، شغّل:
▶ go get github.com/BishopFox/jsluice
بدلاً من استخدام التعبيرات النمطية وحدها، يستخدم jsluice مكتبة go-tree-sitter للبحث عن الأماكن التي يُعرف أن عناوين URL تُستخدم فيها،
مثل تعيينها إلى document.location، أو تمريرها إلى window.open()، أو تمريرها إلى fetch() وما إلى ذلك.
يوجد برنامج مثال بسيط هنا:
analyzer := jsluice.NewAnalyzer([]byte(`
const login = (redirect) => {
document.location = "/login?redirect=" + redirect + "&method=oauth"
}
`))
for _, url := range analyzer.GetURLs() {
j, err := json.MarshalIndent(url, "", " ")
if err != nil {
continue
}
fmt.Printf("%s\n", j)
}
تشغيل المثال:
▶ go run examples/basic/main.go
{
"url": "/login?redirect=EXPR\u0026method=oauth",
"queryParams": [
"method",
"redirect"
],
"bodyParams": [],
"method": "GET",
"type": "locationAssignment",
"source": "document.location = \"/login?redirect=\" + redirect + \"\u0026method=oauth\""
}
لاحظ أن قيمة معامل سلسلة الاستعلام redirect هي EXPR.
الكود مثل هذا شائع في JavaScript:
document.location = "/login?redirect=" + redirect + "&method=oauth"
jsluice يفهم تسلسل السلاسل النصية، ويستبدل أي تعبيرات لا يمكنه معرفة قيمتها
بـ EXPR. على الرغم من أن هذا ليس حلاً مضمونًا، إلا أن هذا الأسلوب ينتج عنوان URL أو مسارًا صالحًا
في أغلب الأحيان، ويعني أنه من الممكن اكتشاف أشياء لا يسهل العثور عليها باستخدام
أساليب أخرى. في هذه الحالة، قد يفوت التعبير النمطي الساذج معامل سلسلة الاستعلام
method:
▶ JS='document.location = "/login?redirect=" + redirect + "&method=oauth"'
▶ echo $JS | grep -oE 'document\.location = "[^"]+"'
document.location = "/login?redirect="
يأتي jsluice مع بعض مطابقات URL المدمجة للسيناريوهات الشائعة، ولكن يمكنك إضافة المزيد
باستخدام دالة AddURLMatcher:
analyzer := jsluice.NewAnalyzer([]byte(`
var fn = () => {
var meta = {
contact: "mailto:[email protected]",
home: "https://example.com"
}
return meta
}
`))
analyzer.AddURLMatcher(
// The first value in the jsluice.URLMatcher struct is the type of node to look for.
// It can be one of "string", "assignment_expression", or "call_expression"
jsluice.URLMatcher{"string", func(n *jsluice.Node) *jsluice.URL {
val := n.DecodedString()
if !strings.HasPrefix(val, "mailto:") {
return nil
}
return &jsluice.URL{
URL: val,
Type: "mailto",
}
}},
)
for _, match := range analyzer.GetURLs() {
fmt.Println(match.URL)
}
توجد نسخة من هذا المثال هنا. يمكنك تشغيله على النحو التالي:
▶ go run examples/urlmatcher/main.go
mailto:[email protected]
https://example.com
لا يطابق jsluice عناوين URI بصيغة mailto: افتراضيًا، وقد تم العثور عليها بواسطة URLMatcher المخصص.
بالإضافة إلى عناوين URL، يمكن لـ jsluice استخراج الأسرار. كما هو الحال مع استخراج URL، يمكن
توفير مطابقات مخصصة لاستكمال المطابقات الافتراضية. يوجد برنامج مثال قصير هنا
يقوم بذلك تمامًا:
analyzer := jsluice.NewAnalyzer([]byte(`
var config = {
apiKey: "AUTH_1a2b3c4d5e6f",
apiURL: "https://api.example.com/v2/"
}
`))
analyzer.AddSecretMatcher(
// The first value in the jsluice.SecretMatcher struct is a
// tree-sitter query to run on the JavaScript source.
jsluice.SecretMatcher{"(pair) @match", func(n *jsluice.Node) *jsluice.Secret {
key := n.ChildByFieldName("key").DecodedString()
value := n.ChildByFieldName("value").DecodedString()
if !strings.Contains(key, "api") {
return nil
}
if !strings.HasPrefix(value, "AUTH_") {
return nil
}
return &jsluice.Secret{
Kind: "fakeApi",
Data: map[string]string{
"key": key,
"value": value,
},
Severity: jsluice.SeverityLow,
Context: n.Parent().AsMap(),
}
}},
)
for _, match := range analyzer.GetSecrets() {
j, err := json.MarshalIndent(match, "", " ")
if err != nil {
continue
}
fmt.Printf("%s\n", j)
}
تشغيل المثال:
▶ go run examples/secrets/main.go
[2023-06-14T13:04:16+0100]
{
"kind": "fakeApi",
"data": {
"key": "apiKey",
"value": "AUTH_1a2b3c4d5e6f"
},
"severity": "low",
"context": {
"apiKey": "AUTH_1a2b3c4d5e6f",
"apiURL": "https://api.example.com/v2/"
}
}
نظرًا لأن لدينا شجرة بناء جملة متاحة لكامل مصدر JavaScript،
كان من الممكن فحص كل من key و value، وتوفير الكائن الأصلي بسهولة كسياق للمطابقة.