Skip to content

Fix JSONPath ambiguity check in ALLOW mode#323

Open
Breus wants to merge 2 commits into
masterfrom
fix/allow-mode-jsonpath-ambiguity-check
Open

Fix JSONPath ambiguity check in ALLOW mode#323
Breus wants to merge 2 commits into
masterfrom
fix/allow-mode-jsonpath-ambiguity-check

Conversation

@Breus

@Breus Breus commented Jun 12, 2026

Copy link
Copy Markdown
Owner

Problem

In ALLOW mode, JSONPaths added via maskJsonPaths(path, KeyMaskingConfig) are stored only in targetKeyConfigs (not in targetJsonPaths). Since build() only called checkAmbiguity(targetJsonPaths), these paths were never validated for ambiguity — neither against each other, nor against allowJsonPaths entries.

For example, this config should throw but didn't:

JsonMaskingConfig.builder()
    .allowJsonPaths("$.a.b.c")
    .maskJsonPaths("$.a.*.c", KeyMaskingConfig.builder().build())
    .build() // should throw: ambiguity between $.a.b.c and $.a.*.c

Fix

  • JsonMaskingConfig.build(): Merge JSONPath entries from targetKeyConfigs (using tryParse to skip plain key names) into the ambiguity check set before calling checkAmbiguity.
  • Javadoc: Added documentation to targetKeyConfigs (both outer class and builder fields) clarifying that it contains both plain key names and JSONPath strings.
  • Tests: Added two test cases to JsonMaskingConfigTest.invalidBuilders():
    1. Ambiguity between allowJsonPaths and maskJsonPaths(path, config)
    2. Ambiguity between two maskJsonPaths(path, config) calls in ALLOW mode
  • README: Minor wording update.

Fixes #318

@github-actions

github-actions Bot commented Jun 12, 2026

Copy link
Copy Markdown

Note

These results are affected by shared workloads on GitHub runners. Use the results only to detect possible regressions, but always rerun on a more stable machine before drawing conclusions!
Regressions/improvements are highlighted when the difference exceeds 3.0%.

Benchmark results

BaselineBenchmark

Method characters jsonSize maskedKeyProbability master (ops/s) PR (ops/s) change master alloc (B/op) PR alloc (B/op) alloc change
regexReplace unicode 1kb 0.1 4,100 5,737 🟢 +39.9% 53,048.6 53,048.4 ⚪ -0.0%
writeFile unicode 1kb 0.1 5,474 5,307 🔴 -3.0% 5,920.4 5,920.5 ⚪ +0.0%
Full results — BaselineBenchmark
Method characters jsonSize maskedKeyProbability master (ops/s) PR (ops/s) change master alloc (B/op) PR alloc (B/op) alloc change
countBytes unicode 1kb 0.1 2,761,755 2,758,547 ⚪ -0.1% 0.0010 0.0010 ⚪ +0.0%
jacksonParseAndMask unicode 1kb 0.1 38,646 38,359 ⚪ -0.7% 58,536.3 58,496.1 ⚪ -0.1%
jacksonParseOnly unicode 1kb 0.1 87,166 88,655 ⚪ +1.7% 17,616.0 17,616.0 ⚪ -0.0%
regexReplace unicode 1kb 0.1 4,100 5,737 🟢 +39.9% 53,048.6 53,048.4 ⚪ -0.0%
writeFile unicode 1kb 0.1 5,474 5,307 🔴 -3.0% 5,920.4 5,920.5 ⚪ +0.0%

InstanceCreationBenchmark

No significant changes (all within 3.0%).

Full results — InstanceCreationBenchmark
Method numberOfTargetKeys master (ops/s) PR (ops/s) change master alloc (B/op) PR alloc (B/op) alloc change
jsonMasker 1000 1,584 1,577 ⚪ -0.4% 1,637,291 1,637,331 ⚪ +0.0%

JsonMaskerBenchmark

Method characters jsonPath jsonSize maskedKeyProbability master (ops/s) PR (ops/s) change master alloc (B/op) PR alloc (B/op) alloc change
jsonMaskerByteArrayStreams unicode true 1kb 0.1 225,989 301,038 🟢 +33.2% 12,184.0 12,184.0 ⚪ -0.0%
Full results — JsonMaskerBenchmark
Method characters jsonPath jsonSize maskedKeyProbability master (ops/s) PR (ops/s) change master alloc (B/op) PR alloc (B/op) alloc change
jsonMaskerByteArrayStreams unicode false 1kb 0.1 241,814 246,270 ⚪ +1.8% 10,840.0 10,840.0 ⚪ -0.0%
jsonMaskerByteArrayStreams unicode true 1kb 0.1 225,989 301,038 🟢 +33.2% 12,184.0 12,184.0 ⚪ -0.0%
jsonMaskerBytes unicode false 1kb 0.1 441,217 449,603 ⚪ +1.9% 2,272.0 2,272.0 ⚪ +0.0%
jsonMaskerBytes unicode true 1kb 0.1 445,005 437,937 ⚪ -1.6% 2,024.0 2,024.0 ⚪ +0.0%
jsonMaskerString unicode false 1kb 0.1 235,718 238,835 ⚪ +1.3% 10,176.0 10,176.0 ⚪ +0.0%
jsonMaskerString unicode true 1kb 0.1 213,817 209,535 ⚪ -2.0% 10,944.0 10,944.0 ⚪ +0.0%

LargeKeySetInstanceCreationBenchmark

No significant changes (all within 3.0%).

Full results — LargeKeySetInstanceCreationBenchmark
Method keyLength numberOfTargetKeys master (ops/s) PR (ops/s) change master alloc (B/op) PR alloc (B/op) alloc change
jsonMasker 100 1000 133.42 132.75 ⚪ -0.5% 32,372,330 32,372,516 ⚪ +0.0%

StreamTypeBenchmark

Method jsonSize streamInputType streamOutputType master (ops/s) PR (ops/s) change master alloc (B/op) PR alloc (B/op) alloc change
jsonMaskerStreams 1kb ByteArrayStream FileStream 4,966 4,590 🔴 -7.6% 9,208.5 9,208.5 ⚪ +0.0%
Full results — StreamTypeBenchmark
Method jsonSize streamInputType streamOutputType master (ops/s) PR (ops/s) change master alloc (B/op) PR alloc (B/op) alloc change
jsonMaskerStreams 1kb ByteArrayStream ByteArrayStream 229,490 227,114 ⚪ -1.0% 12,184.0 12,184.0 ⚪ +0.0%
jsonMaskerStreams 1kb ByteArrayStream FileStream 4,966 4,590 🔴 -7.6% 9,208.5 9,208.5 ⚪ +0.0%
jsonMaskerStreams 1kb FileStream ByteArrayStream 93,527 92,056 ⚪ -1.6% 12,336.0 12,304.0 ⚪ -0.3%
jsonMaskerStreams 1kb FileStream FileStream 4,804 4,835 ⚪ +0.6% 9,392.5 9,392.5 ⚪ -0.0%

ValueMaskerBenchmark

Method characters jsonSize maskedKeyProbability master (ops/s) PR (ops/s) change master alloc (B/op) PR alloc (B/op) alloc change
maskWithStatic unicode 1kb 0.1 663,580 705,564 🟢 +6.3% 1,272.0 1,272.0 ⚪ -0.0%
maskWithTextValueFunction unicode 1kb 0.1 544,845 565,819 🟢 +3.8% 1,776.0 1,776.0 ⚪ +0.0%
Full results — ValueMaskerBenchmark
Method characters jsonSize maskedKeyProbability master (ops/s) PR (ops/s) change master alloc (B/op) PR alloc (B/op) alloc change
maskWithRawValueFunction unicode 1kb 0.1 616,955 616,751 ⚪ -0.0% 1,632.0 1,632.0 ⚪ +0.0%
maskWithStatic unicode 1kb 0.1 663,580 705,564 🟢 +6.3% 1,272.0 1,272.0 ⚪ -0.0%
maskWithTextValueFunction unicode 1kb 0.1 544,845 565,819 🟢 +3.8% 1,776.0 1,776.0 ⚪ +0.0%
Raw output (PR @ e6e91fd)
Benchmark                                                           (characters)  (jsonPath)  (jsonSize)  (keyLength)  (maskedKeyProbability)  (numberOfTargetKeys)  (streamInputType)  (streamOutputType)   Mode  Cnt         Score       Error   Units
BaselineBenchmark.countBytes                                             unicode         N/A         1kb          N/A                     0.1                   N/A                N/A                 N/A  thrpt    4   2758547.161 ± 67325.324   ops/s
BaselineBenchmark.jacksonParseAndMask                                    unicode         N/A         1kb          N/A                     0.1                   N/A                N/A                 N/A  thrpt    4     38358.892 ±   634.306   ops/s
BaselineBenchmark.jacksonParseOnly                                       unicode         N/A         1kb          N/A                     0.1                   N/A                N/A                 N/A  thrpt    4     88655.064 ±   656.565   ops/s
BaselineBenchmark.regexReplace                                           unicode         N/A         1kb          N/A                     0.1                   N/A                N/A                 N/A  thrpt    4      5736.512 ±   107.835   ops/s
BaselineBenchmark.writeFile                                              unicode         N/A         1kb          N/A                     0.1                   N/A                N/A                 N/A  thrpt    4      5307.452 ±  4166.554   ops/s
InstanceCreationBenchmark.jsonMasker                                         N/A         N/A         N/A          N/A                     N/A                  1000                N/A                 N/A  thrpt    4      1577.271 ±    82.596   ops/s
JsonMaskerBenchmark.jsonMaskerByteArrayStreams                           unicode       false         1kb          N/A                     0.1                   N/A                N/A                 N/A  thrpt    4    246270.081 ±  4001.930   ops/s
JsonMaskerBenchmark.jsonMaskerByteArrayStreams                           unicode        true         1kb          N/A                     0.1                   N/A                N/A                 N/A  thrpt    4    301038.425 ± 14969.130   ops/s
JsonMaskerBenchmark.jsonMaskerBytes                                      unicode       false         1kb          N/A                     0.1                   N/A                N/A                 N/A  thrpt    4    449602.910 ± 27643.172   ops/s
JsonMaskerBenchmark.jsonMaskerBytes                                      unicode        true         1kb          N/A                     0.1                   N/A                N/A                 N/A  thrpt    4    437937.348 ±  4662.627   ops/s
JsonMaskerBenchmark.jsonMaskerString                                     unicode       false         1kb          N/A                     0.1                   N/A                N/A                 N/A  thrpt    4    238834.862 ±  2517.586   ops/s
JsonMaskerBenchmark.jsonMaskerString                                     unicode        true         1kb          N/A                     0.1                   N/A                N/A                 N/A  thrpt    4    209535.484 ±   812.024   ops/s
LargeKeySetInstanceCreationBenchmark.jsonMasker                              N/A         N/A         N/A          100                     N/A                  1000                N/A                 N/A  thrpt    4       132.747 ±     4.922   ops/s
StreamTypeBenchmark.jsonMaskerStreams                                        N/A         N/A         1kb          N/A                     N/A                   N/A    ByteArrayStream     ByteArrayStream  thrpt    4    227113.861 ±  3136.892   ops/s
StreamTypeBenchmark.jsonMaskerStreams                                        N/A         N/A         1kb          N/A                     N/A                   N/A    ByteArrayStream          FileStream  thrpt    4      4589.793 ±  2990.508   ops/s
StreamTypeBenchmark.jsonMaskerStreams                                        N/A         N/A         1kb          N/A                     N/A                   N/A         FileStream     ByteArrayStream  thrpt    4     92055.540 ±  1686.683   ops/s
StreamTypeBenchmark.jsonMaskerStreams                                        N/A         N/A         1kb          N/A                     N/A                   N/A         FileStream          FileStream  thrpt    4      4834.576 ±   827.516   ops/s
ValueMaskerBenchmark.maskWithRawValueFunction                            unicode         N/A         1kb          N/A                     0.1                   N/A                N/A                 N/A  thrpt    4    616751.409 ± 10246.940   ops/s
ValueMaskerBenchmark.maskWithStatic                                      unicode         N/A         1kb          N/A                     0.1                   N/A                N/A                 N/A  thrpt    4    705564.001 ±  8509.533   ops/s
ValueMaskerBenchmark.maskWithTextValueFunction                           unicode         N/A         1kb          N/A                     0.1                   N/A                N/A                 N/A  thrpt    4    565818.850 ±  3253.005   ops/s
Raw output (master @ ea41a65)
Benchmark                                                           (characters)  (jsonPath)  (jsonSize)  (keyLength)  (maskedKeyProbability)  (numberOfTargetKeys)  (streamInputType)  (streamOutputType)   Mode  Cnt         Score       Error   Units
BaselineBenchmark.countBytes                                             unicode         N/A         1kb          N/A                     0.1                   N/A                N/A                 N/A  thrpt    4   2761754.932 ± 13000.610   ops/s
BaselineBenchmark.jacksonParseAndMask                                    unicode         N/A         1kb          N/A                     0.1                   N/A                N/A                 N/A  thrpt    4     38645.692 ±   761.468   ops/s
BaselineBenchmark.jacksonParseOnly                                       unicode         N/A         1kb          N/A                     0.1                   N/A                N/A                 N/A  thrpt    4     87166.245 ±   451.024   ops/s
BaselineBenchmark.regexReplace                                           unicode         N/A         1kb          N/A                     0.1                   N/A                N/A                 N/A  thrpt    4      4099.873 ±   137.514   ops/s
BaselineBenchmark.writeFile                                              unicode         N/A         1kb          N/A                     0.1                   N/A                N/A                 N/A  thrpt    4      5473.846 ±  4724.280   ops/s
InstanceCreationBenchmark.jsonMasker                                         N/A         N/A         N/A          N/A                     N/A                  1000                N/A                 N/A  thrpt    4      1583.861 ±    31.932   ops/s
JsonMaskerBenchmark.jsonMaskerByteArrayStreams                           unicode       false         1kb          N/A                     0.1                   N/A                N/A                 N/A  thrpt    4    241814.412 ±  7247.554   ops/s
JsonMaskerBenchmark.jsonMaskerByteArrayStreams                           unicode        true         1kb          N/A                     0.1                   N/A                N/A                 N/A  thrpt    4    225989.399 ±  9750.475   ops/s
JsonMaskerBenchmark.jsonMaskerBytes                                      unicode       false         1kb          N/A                     0.1                   N/A                N/A                 N/A  thrpt    4    441216.517 ±  2355.531   ops/s
JsonMaskerBenchmark.jsonMaskerBytes                                      unicode        true         1kb          N/A                     0.1                   N/A                N/A                 N/A  thrpt    4    445005.023 ± 10072.865   ops/s
JsonMaskerBenchmark.jsonMaskerString                                     unicode       false         1kb          N/A                     0.1                   N/A                N/A                 N/A  thrpt    4    235718.490 ±  2680.669   ops/s
JsonMaskerBenchmark.jsonMaskerString                                     unicode        true         1kb          N/A                     0.1                   N/A                N/A                 N/A  thrpt    4    213817.031 ±  1615.076   ops/s
LargeKeySetInstanceCreationBenchmark.jsonMasker                              N/A         N/A         N/A          100                     N/A                  1000                N/A                 N/A  thrpt    4       133.418 ±     4.331   ops/s
StreamTypeBenchmark.jsonMaskerStreams                                        N/A         N/A         1kb          N/A                     N/A                   N/A    ByteArrayStream     ByteArrayStream  thrpt    4    229489.566 ±  3392.531   ops/s
StreamTypeBenchmark.jsonMaskerStreams                                        N/A         N/A         1kb          N/A                     N/A                   N/A    ByteArrayStream          FileStream  thrpt    4      4966.292 ±  1651.691   ops/s
StreamTypeBenchmark.jsonMaskerStreams                                        N/A         N/A         1kb          N/A                     N/A                   N/A         FileStream     ByteArrayStream  thrpt    4     93527.463 ±  4024.631   ops/s
StreamTypeBenchmark.jsonMaskerStreams                                        N/A         N/A         1kb          N/A                     N/A                   N/A         FileStream          FileStream  thrpt    4      4804.458 ±  1165.811   ops/s
ValueMaskerBenchmark.maskWithRawValueFunction                            unicode         N/A         1kb          N/A                     0.1                   N/A                N/A                 N/A  thrpt    4    616954.545 ±  9701.176   ops/s
ValueMaskerBenchmark.maskWithStatic                                      unicode         N/A         1kb          N/A                     0.1                   N/A                N/A                 N/A  thrpt    4    663579.595 ± 19743.815   ops/s
ValueMaskerBenchmark.maskWithTextValueFunction                           unicode         N/A         1kb          N/A                     0.1                   N/A                N/A                 N/A  thrpt    4    544845.064 ±  6914.072   ops/s

In ALLOW mode, JSONPaths added via maskJsonPaths(path, KeyMaskingConfig)
were only stored in targetKeyConfigs and not in targetJsonPaths. Since
build() only checked targetJsonPaths for ambiguity, these paths were
never validated — neither against each other nor against allowJsonPaths
entries.

The fix merges JSONPath entries from targetKeyConfigs into the ambiguity
check set using tryParse (which skips plain key names). Also adds
Javadoc to targetKeyConfigs clarifying that it contains both plain key
names and JSONPath strings.

Fixes #318
@Breus Breus force-pushed the fix/allow-mode-jsonpath-ambiguity-check branch from f4674e1 to 966553d Compare June 12, 2026 18:08
@Breus Breus requested review from donavdey and gavlyukovskiy June 12, 2026 18:08
@Breus Breus enabled auto-merge (squash) June 12, 2026 18:21
@Breus Breus force-pushed the fix/allow-mode-jsonpath-ambiguity-check branch from ac2b8c5 to de595ab Compare June 12, 2026 18:55
…tKeyConfigs

Replace the tryParse-based approach with a dedicated customConfigJsonPaths
set that tracks JSONPaths added via maskJsonPaths(path, config). This
avoids reparsing strings at build time and is consistent with the
existing split between targetKeys and targetJsonPaths.

Also adds positive test cases to verify that non-ambiguous ALLOW mode
combinations and plain key configs are not affected.
@Breus Breus force-pushed the fix/allow-mode-jsonpath-ambiguity-check branch from de595ab to e6e91fd Compare June 12, 2026 18:56
@sonarqubecloud

Copy link
Copy Markdown

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

ALLOW mode does not check for masked JSON Paths ambiguity.

1 participant