mirror of
https://github.com/j3ssie/osmedeus.git
synced 2026-09-22 17:44:59 +02:00
154 lines
4.0 KiB
Go
154 lines
4.0 KiB
Go
package functions
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"os"
|
|
|
|
"github.com/dop251/goja"
|
|
"github.com/itchyny/gojq"
|
|
"github.com/j3ssie/osmedeus/v5/internal/logger"
|
|
"go.uber.org/zap"
|
|
)
|
|
|
|
// jq executes a jq query on JSON data
|
|
// Usage: jq(jsonData, query)
|
|
// Examples:
|
|
// - jq('{"url": "example.com"}', '.url') -> "example.com"
|
|
// - jq('{"items": [1, 2, 3]}', '.items[]') -> [1, 2, 3]
|
|
// - jq('{"data": {"status": "ok"}}', '.data.status') -> "ok"
|
|
func (vf *vmFunc) jq(call goja.FunctionCall) goja.Value {
|
|
jsonData := call.Argument(0).String()
|
|
query := call.Argument(1).String()
|
|
logger.Get().Debug("Calling jq", zap.String("query", query), zap.Int("dataLength", len(jsonData)))
|
|
|
|
if jsonData == "undefined" || jsonData == "" {
|
|
logger.Get().Warn("jq: empty JSON data provided")
|
|
return vf.vm.ToValue("")
|
|
}
|
|
|
|
if query == "undefined" || query == "" {
|
|
logger.Get().Warn("jq: empty query provided")
|
|
return vf.vm.ToValue("")
|
|
}
|
|
|
|
// Parse JSON data
|
|
var data interface{}
|
|
if err := json.Unmarshal([]byte(jsonData), &data); err != nil {
|
|
logger.Get().Debug("jq: input is not valid JSON, treating as raw string", zap.Error(err))
|
|
// If not valid JSON, treat as raw string
|
|
data = jsonData
|
|
}
|
|
|
|
// Parse jq query
|
|
q, err := gojq.Parse(query)
|
|
if err != nil {
|
|
logger.Get().Warn("jq: invalid query syntax", zap.String("query", query), zap.Error(err))
|
|
return vf.vm.ToValue("")
|
|
}
|
|
|
|
// Execute query
|
|
iter := q.Run(data)
|
|
v, ok := iter.Next()
|
|
if !ok {
|
|
logger.Get().Debug("jq: query returned no results", zap.String("query", query))
|
|
return vf.vm.ToValue("")
|
|
}
|
|
|
|
// Check for error
|
|
if err, isErr := v.(error); isErr {
|
|
logger.Get().Warn("jq: query execution error", zap.String("query", query), zap.Error(err))
|
|
return vf.vm.ToValue(fmt.Sprintf("error: %s", err.Error()))
|
|
}
|
|
|
|
logger.Get().Debug("jq result", zap.String("query", query), zap.Any("result", v))
|
|
// Convert result to appropriate type
|
|
return vf.vm.ToValue(v)
|
|
}
|
|
|
|
func (vf *vmFunc) jqFromFile(call goja.FunctionCall) goja.Value {
|
|
path := call.Argument(0).String()
|
|
query := call.Argument(1).String()
|
|
logger.Get().Debug("Calling jqFromFile", zap.String("path", path), zap.String("query", query))
|
|
|
|
if path == "undefined" || path == "" {
|
|
logger.Get().Warn("jqFromFile: empty path provided")
|
|
return vf.vm.ToValue("")
|
|
}
|
|
if query == "undefined" || query == "" {
|
|
logger.Get().Warn("jqFromFile: empty query provided")
|
|
return vf.vm.ToValue("")
|
|
}
|
|
|
|
content, err := os.ReadFile(path)
|
|
if err != nil {
|
|
logger.Get().Warn("jqFromFile: failed to read file", zap.String("path", path), zap.Error(err))
|
|
return vf.vm.ToValue("")
|
|
}
|
|
|
|
v, err := JQ(string(content), query)
|
|
if err != nil {
|
|
logger.Get().Warn("jqFromFile: query execution error", zap.String("query", query), zap.Error(err))
|
|
return vf.vm.ToValue(fmt.Sprintf("error: %s", err.Error()))
|
|
}
|
|
if v == nil {
|
|
return vf.vm.ToValue("")
|
|
}
|
|
|
|
return vf.vm.ToValue(v)
|
|
}
|
|
|
|
// JQ is a standalone function for jq queries (can be used outside Otto)
|
|
// Usage: JQ(jsonData, query) -> (interface{}, error)
|
|
func JQ(jsonData string, query string) (interface{}, error) {
|
|
if jsonData == "" || query == "" {
|
|
return nil, nil
|
|
}
|
|
|
|
// Parse JSON data
|
|
var data interface{}
|
|
if err := json.Unmarshal([]byte(jsonData), &data); err != nil {
|
|
return nil, fmt.Errorf("invalid JSON: %w", err)
|
|
}
|
|
|
|
// Parse jq query
|
|
q, err := gojq.Parse(query)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("invalid jq query: %w", err)
|
|
}
|
|
|
|
// Execute query
|
|
iter := q.Run(data)
|
|
v, ok := iter.Next()
|
|
if !ok {
|
|
return nil, nil
|
|
}
|
|
|
|
// Check for error
|
|
if err, isErr := v.(error); isErr {
|
|
return nil, err
|
|
}
|
|
|
|
return v, nil
|
|
}
|
|
|
|
// JQString is a convenience function that returns the result as a string
|
|
func JQString(jsonData string, query string) string {
|
|
result, err := JQ(jsonData, query)
|
|
if err != nil || result == nil {
|
|
return ""
|
|
}
|
|
|
|
switch v := result.(type) {
|
|
case string:
|
|
return v
|
|
default:
|
|
// Convert to JSON string for complex types
|
|
b, err := json.Marshal(v)
|
|
if err != nil {
|
|
return fmt.Sprintf("%v", v)
|
|
}
|
|
return string(b)
|
|
}
|
|
}
|