Skip to content
Merged
Show file tree
Hide file tree
Changes from 12 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Mocai

![mocai](img/mocai-mascot.png)
![mocai](img/mocai.svg)

#### [README-PT](/docs/localization/pt/README-PT.md)

Expand Down
2 changes: 1 addition & 1 deletion docs/localization/pt/README-PT.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Mocai

![mocai](../../.././img/mocai-mascot.png)
![mocai](../../.././img/mocai.svg)

Uma biblioteca Go para geração de dados de teste, permitindo criar mocks de entidades de forma simples e eficiente.

Expand Down
30 changes: 24 additions & 6 deletions examples/mock_example.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"github.com/brazzcore/mocai/pkg/mocai/entities/address"
"github.com/brazzcore/mocai/pkg/mocai/entities/certificate"
"github.com/brazzcore/mocai/pkg/mocai/entities/company"
"github.com/brazzcore/mocai/pkg/mocai/entities/national_id"
"github.com/brazzcore/mocai/pkg/mocai/entities/person"
"github.com/brazzcore/mocai/pkg/mocai/entities/phone"
"github.com/brazzcore/mocai/pkg/mocai/entities/vote_registration"
Expand Down Expand Up @@ -51,25 +52,42 @@ func GenerateMockExample() {
return
}

nationalID_mock, err := national_id.GenerateNationalID(false)
if err != nil {
fmt.Print(err)
return
}

fmt.Println(constants.HeaderMain)
fmt.Println(constants.SubHeader)

fmt.Printf("Person: %s %s, %s, %d years old, CPF: %s\n",
person_mock.FirstNameMale, person_mock.LastName, person_mock.Gender, person_mock.Age, person_mock.CPF)

fmt.Printf("Birth Certificate: %s\n", certificate_mock.BrazilianCertificates.BirthCertificate.CertificateNumber)
fmt.Printf("RG: %s, State: %s, Issuing Body: %s\n",
nationalID_mock.BrazilianRG.Number, nationalID_mock.BrazilianRG.State, nationalID_mock.BrazilianRG.IssuingBody)

fmt.Printf("Birth Certificate: %s\n",
certificate_mock.BrazilianCertificates.BirthCertificate.CertificateNumber)

fmt.Printf("Marriage Certificate: %s\n", certificate_mock.BrazilianCertificates.MarriageCertificate.CertificateNumber)
fmt.Printf("Marriage Certificate: %s\n",
certificate_mock.BrazilianCertificates.MarriageCertificate.CertificateNumber)

fmt.Printf("Death Certificate: %s\n", certificate_mock.BrazilianCertificates.DeathCertificate.CertificateNumber)
fmt.Printf("Death Certificate: %s\n",
certificate_mock.BrazilianCertificates.DeathCertificate.CertificateNumber)

fmt.Printf("Company: %s, CNPJ: %s\n", company_mock.BrazilianCompany.CompanyName, company_mock.BrazilianCompany.CNPJ)
fmt.Printf("Company: %s, CNPJ: %s\n",
company_mock.BrazilianCompany.CompanyName, company_mock.BrazilianCompany.CNPJ)

fmt.Printf("Address: %s, %d - %s, %s (%s) - %s\n",
address_mock.Street, address_mock.Number, address_mock.City, address_mock.State, address_mock.UF, address_mock.ZIP)
fmt.Printf("Phone: (%s) %s\n", phone_mock.AreaCode, phone_mock.Number)

fmt.Printf("Voter Registration Card: Section: %s, Zone: %s, Registration: %s\n", vote_registration_mock.BrazilianVoteRegistration.Section, vote_registration_mock.BrazilianVoteRegistration.Zone, vote_registration_mock.BrazilianVoteRegistration.Number)
fmt.Printf("Phone: (%s) %s\n",
phone_mock.AreaCode, phone_mock.Number)

fmt.Printf("Voter Registration Card: Section: %s, Zone: %s, Registration: %s\n",
vote_registration_mock.BrazilianVoteRegistration.Section, vote_registration_mock.BrazilianVoteRegistration.Zone,
vote_registration_mock.BrazilianVoteRegistration.Number)

fmt.Println(constants.Footer)
}
Binary file removed img/mocai-mascot.png
Binary file not shown.
1 change: 1 addition & 0 deletions img/mocai.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
77 changes: 77 additions & 0 deletions pkg/mocai/entities/national_id/countries/brazilian_national_id.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
package countries

import (
"fmt"
"math/rand"
"strconv"
"time"
)

var globalRand = rand.New(rand.NewSource(time.Now().UnixNano()))
Copy link
Copy Markdown

@coderabbitai coderabbitai bot Apr 3, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Security risk with math/rand for ID generation.

Using math/rand for generating identity documents is not secure enough as it's predictable. For security-sensitive data like national IDs, crypto/rand should be used instead.

-import (
-	"fmt"
-	"math/rand"
-	"strconv"
-	"time"
-)
+import (
+	"crypto/rand"
+	"fmt"
+	"math/big"
+	"strconv"
+	"time"
+)

-var globalRand = rand.New(rand.NewSource(time.Now().UnixNano()))
+// No need for a global variable with crypto/rand

Then update the calculateSPRGDigit function:

func calculateSPRGDigit() (string, error) {
	// generate a random base until 8 digits
-	base := globalRand.Intn(100000000)
-	baseStr := fmt.Sprintf("%08d", base)
+	max := big.NewInt(100000000)
+	n, err := rand.Int(rand.Reader, max)
+	if err != nil {
+		return "", ErrGeneratingBrazilianNationalID
+	}
+	baseStr := fmt.Sprintf("%08d", n.Int64())
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
var globalRand = rand.New(rand.NewSource(time.Now().UnixNano()))
package brazilian_national_id
import (
"crypto/rand"
"fmt"
"math/big"
"strconv"
"time"
)
// No need for a global variable with crypto/rand
func calculateSPRGDigit() (string, error) {
// generate a random base until 8 digits
max := big.NewInt(100000000)
n, err := rand.Int(rand.Reader, max)
if err != nil {
return "", ErrGeneratingBrazilianNationalID
}
baseStr := fmt.Sprintf("%08d", n.Int64())
return baseStr, nil
}

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

there’s no need since the data is made up

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@wellfernandes You're right. Since this is just for generating mock data and not real IDs, using math/rand is perfectly fine. No need for the cryptographically secure alternative in this case.

✏️ Learnings added
Learnt from: wellfernandes
PR: brazzcore/mocai#44
File: pkg/mocai/entities/national_id/countries/brazilian_national_id.go:10-10
Timestamp: 2025-04-03T19:13:54.519Z
Learning: For the mocai project, using math/rand is acceptable for generating mock data (like national IDs) since the data is fictitious and not intended for security-sensitive purposes.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.


type RG struct {
Number string
State string
IssuingBody string
}

// GenerateBrazilianNationalID generates a valid Brazilian national ID [RG] for São Paulo.
func GenerateBrazilianNationalID(formatted bool) (*RG, error) {
rgNumber, err := calculateSPRGDigit()
if err != nil {
return nil, err
}
if formatted {
rgNumber = formatRG(rgNumber)
}
return &RG{
Number: rgNumber,
State: "SP",
IssuingBody: "SSP - Secretaria de Seguranca Publica",
}, nil
}

// calculateSPRGDigit generates a valid Brazilian national ID [RG] for São Paulo.
func calculateSPRGDigit() (string, error) {
// generate a random base until 8 digits
base := globalRand.Intn(100000000)
baseStr := fmt.Sprintf("%08d", base)

// slice contains the digits
d := make([]int, 8)
for i := range 8 {
val, err := strconv.Atoi(string(baseStr[i]))
if err != nil {
return "", ErrToConvertDigit
}
d[i] = val
}

// wheights from right to left: 9, 8, 7, 6, 5, 4, 3, 2
wheights := []int{2, 3, 4, 5, 6, 7, 8, 9}

// calculate sum
sum := 0
for i := range 8 {
sum += d[7-i] * wheights[i]
}

// calculate check digit
checkDigit := sum % 11
var dvStr string
if checkDigit == 10 {
dvStr = "X"
} else {
dvStr = strconv.Itoa(checkDigit)
}

full := fmt.Sprintf("%s%s", baseStr, dvStr)
return full, nil
}

func formatRG(rgNumber string) string {
if len(rgNumber) != 9 {
return rgNumber
}
return fmt.Sprintf("%s.%s.%s-%s", rgNumber[0:3], rgNumber[3:6], rgNumber[6:8], rgNumber[8:])
}
8 changes: 8 additions & 0 deletions pkg/mocai/entities/national_id/countries/errors.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package countries

import "errors"

var (
ErrGeneratingBrazilianNationalID = errors.New("error generating brazilian national id")
ErrToConvertDigit = errors.New("error converting digit")
)
19 changes: 19 additions & 0 deletions pkg/mocai/entities/national_id/generator.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package national_id

import "github.com/brazzcore/mocai/pkg/mocai/entities/national_id/countries"

type NationalID struct {
BrazilianRG *countries.RG
}

// GenerateNationalID generates a Brazilian national ID [RG].
// If formatted is true, the Brazilian national ID will be returned in the format XX.XXX.XXX-X.
// if formatted is false, the Brazilian national ID will be returned as a plain string.
func GenerateNationalID(formatted bool) (*NationalID, error) {
rg, err := countries.GenerateBrazilianNationalID(formatted)
if err != nil {
return nil, err
}

return &NationalID{BrazilianRG: rg}, nil
}
Loading