
Research
npm Malware Targets Telegram Bot Developers with Persistent SSH Backdoors
Malicious npm packages posing as Telegram bot libraries install SSH backdoors and exfiltrate data from Linux developer machines.
github.com/baderkha/easygin
A zero magic way to implement DTO pattern with the Gin Framework.
NOTE This package is meant for REST APIs only.
Without easy-gin :
type UserInput struct {
UserID string `json:"user_id" uri:"user_id"`
}
func main(){
en := gin.Default()
en.POST("/:user_id", func(ctx *gin.Context) {
var u UserInput
// bind
err := ctx.BindQuery(&u)
if err != nil {
ctx.JSON(http.StatusBadRequest, err.Error())
return
}
// some extra validation logic
// i know you can add rules in the struct , but this can be replaced with a validation from db ...etc
if u.UserID == "" {
ctx.JSON(http.StatusBadRequest, errors.New("user id is missing"))
return
}
// do somethign with data
ctx.JSON(http.StatusOK, fmt.Sprintf("user with id %s has been processed", u.UserID))
})
}
easy-gin way :
var _ easygin.IRequest = &UserInput{} // this struct implements IRequest
type UserInput struct {
UserID string `json:"user_id" uri:"user_id"` // still use the bind methods from gin !
}
// add custom validation logic not restricted by struct tags
func (u UserInput) Validate() error {
if u.UserID == "" {
return errors.New("user id not set")
}
return nil
}
// you can use this to wrap your error if validation failed from gin or your custom validation
func (u UserInput) ValidationErrorFormat(err error) any {
return map[string]any{
"err": err.Error(),
}
}
func HandleUsers(u UserInput) *easygin.Response {
// do something with the input ...
// focus on your domain logic rather than validation ...etc
return easygin.
Res(fmt.Sprintf("user with id %s has been processed",u.UserID)).
Status(http.StatusOK)
}
func main() {
en := gin.Default()
// by default the second argument is optional
// if not provided it will atempt all bind methods (JSON,QUERY,URI) (this will incur a performance hit)
en.POST("/:user_id", easygin.To(HandleUsers,easygin.BindURI))
en.Run(":80")
}
Note you need golang v1.8 and above to install this utility as under the hood it uses generics
go get -u github.com/baderkha/easy-gin/v1/easygin
Step 1 : Create a DTO object that implements the easygin.IRequest interface
type UpdateUserRequest struct {
ID string `uri:"user_id"` // regular gin binding from instructions
Name string `json:"name"` // binds from json body
UserType string `form:"user_type"` // binds from query parameter
}
func (u UpdateUserRequest) Validate() error {
// your custom validation here , consider using a struct validator like [go-validator](https://github.com/go-playground/validator)
// also you can just have your validation done via tags if it's simple stuff and just return nil here
return nil
}
func (u UpdateUserRequest) ValidationErrorFormat(err error) any {
// if you want your response to be the error string return
return err.Error()
// if you want your response to be a wrapped with an object (map option)
return map[string]any{
"err":err.Error()
}
// if you want your response to be a wrapped with an object (struct option)
return struct {
Error string `json:"err"`
Message string `json:"server_message"`
}{
Error: err.Error(),
Message: "failed validation",
}
}
Step 2 : Create your easygin Handler
// argument must not be a pointer !
func HandleUserUpdate(u UpdateUserRequest) *easygin.Response {
// process the data
// ....
// once ready to respond to client
res := easygin.Res(map[string]any{"wow":"ok"})
return res // this will default with a 200 response code
return res.Status(201) // you can override it yourself , so you can use this to handle errors
}
Step 3 : Add it to your routes
func main() {
en := gin.Default()
// option a default binding
// although this looks cleaner this will have a performance hit if you do not need to bind from everything else
en.PATCH("/:user_id",easygin.To(HandleUserUpdate))
// option b recommended
// only bind from ...
// preferable ,always define where you're binding from
en.PATCH("/:user_id",easygin.To(HandleUserUpdate,easygin.BindURI,easygin.BindJSON,easygin.BindQuery))
}
That's it , this should now work and bind from all the different parts of the http request
You might be thinking , what if you had an object that is passed by a middleware via ctx.Set(_key,_value)
?
This package will allow you to also bind from that object (key word being object , you cannot use a primitive value)
Example
type LoginInfo struct {
UserID string // must have the same name as your dto field
}
type UserInput struct {
UserID string
}
func (u UserInput) Validate() error {
if u.UserID == "" {
return errors.New("user id not set")
}
return nil
}
func (u UserInput) ValidationErrorFormat(err error) any {
return map[string]any{
"err": err.Error(),
}
}
func AuthMiddleware(ctx *gin.Context) {
ctx.Set("user_login_info", LoginInfo{
UserID: "123",
})
ctx.Next()
}
func HandleUsers(u UserInput) *easygin.Response {
return easygin.Res(u.UserID) // will return back 123 , which was passed from the auth middlewar
}
func main() {
en := gin.Default()
// BindContext expects the key you used when you used the set function
en.GET("_login_info", AuthMiddleware, easygin.To(HandleUsers, easygin.BindContext("user_login_info")))
en.Run(":80")
}
FAQs
Unknown package
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Research
Malicious npm packages posing as Telegram bot libraries install SSH backdoors and exfiltrate data from Linux developer machines.
Security News
pip, PDM, pip-audit, and the packaging library are already adding support for Python’s new lock file format.
Product
Socket's Go support is now generally available, bringing automatic scanning and deep code analysis to all users with Go projects.