• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

mybatis / ehcache-cache / 578

28 Feb 2026 09:16PM UTC coverage: 72.581% (-4.2%) from 76.812%
578

Pull #277

github

web-flow
Merge 010e27e4d into 086111212
Pull Request #277: Migrate to Ehcache 3 with hash-flooding DoS protection

34 of 38 branches covered (89.47%)

64 of 82 new or added lines in 4 files covered. (78.05%)

90 of 124 relevant lines covered (72.58%)

0.73 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

89.47
/src/main/java/org/mybatis/caches/ehcache/HashKeyWrapper.java
1
/*
2
 *    Copyright 2010-2026 the original author or authors.
3
 *
4
 *    Licensed under the Apache License, Version 2.0 (the "License");
5
 *    you may not use this file except in compliance with the License.
6
 *    You may obtain a copy of the License at
7
 *
8
 *       https://www.apache.org/licenses/LICENSE-2.0
9
 *
10
 *    Unless required by applicable law or agreed to in writing, software
11
 *    distributed under the License is distributed on an "AS IS" BASIS,
12
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
 *    See the License for the specific language governing permissions and
14
 *    limitations under the License.
15
 */
16
package org.mybatis.caches.ehcache;
17

18
import java.io.Serializable;
19

20
/**
21
 * Key wrapper that applies Murmur3 32-bit finalizer bit-mixing to {@link Object#hashCode()} before the value is exposed
22
 * to Ehcache 3's internal hash structures.
23
 * <p>
24
 * Ehcache 3 uses the raw {@code hashCode()} of stored keys without any internal spreading. When the cache is
25
 * <em>bounded</em> (i.e. a maximum entry count or size has been configured), an attacker who can influence the cache
26
 * keys — for example by controlling SQL query parameters that end up in MyBatis's {@code CacheKey} — can craft a set of
27
 * keys that all share the same raw hash, degrading every cache operation from O(1) to O(n) and causing a
28
 * <strong>hash-flooding denial of service</strong>.
29
 * </p>
30
 * <p>
31
 * Wrapping every key in this class ensures that Ehcache 3 always sees a <em>mixed</em> hash value. The mixing function
32
 * (Murmur3 fmix32) has strong avalanche properties: a single-bit difference in the input changes roughly half of the
33
 * output bits, making it computationally infeasible to produce many keys with the same mixed hash.
34
 * </p>
35
 *
36
 * @see <a href="https://github.com/jhipster/generator-jhipster/issues/28546">jhipster/generator-jhipster #28546</a>
37
 * @see <a href="https://github.com/mybatis/ehcache-cache/issues/61">mybatis/ehcache-cache #61</a>
38
 */
39
final class HashKeyWrapper implements Serializable {
40

41
  private static final long serialVersionUID = 1L;
42

43
  /** The original, unwrapped cache key. */
44
  private final Object key;
45

46
  /**
47
   * The pre-mixed hash code, computed once at construction time. Using a {@code final} field avoids re-computing the
48
   * mixing on every lookup.
49
   */
50
  private final int hash;
51

52
  /**
53
   * Wraps {@code key}, pre-computing its mixed hash.
54
   *
55
   * @param key
56
   *          the original cache key; may be {@code null}
57
   */
58
  HashKeyWrapper(Object key) {
1✔
59
    this.key = key;
1✔
60
    this.hash = fmix32(key == null ? 0 : key.hashCode());
1✔
61
  }
1✔
62

63
  /**
64
   * Returns the original cache key that this wrapper was constructed with.
65
   *
66
   * @return the original key, possibly {@code null}
67
   */
68
  Object getKey() {
NEW
69
    return key;
×
70
  }
71

72
  /**
73
   * Returns the pre-mixed hash code of the wrapped key.
74
   * <p>
75
   * The value is derived by applying the Murmur3 32-bit finalizer (fmix32) to {@code key.hashCode()}. The finalizer has
76
   * excellent avalanche properties: every output bit depends on every input bit, so an attacker who knows the raw
77
   * {@code hashCode()} of a key cannot predict which Ehcache 3 internal bucket the mixed hash will land in.
78
   * </p>
79
   */
80
  @Override
81
  public int hashCode() {
82
    return hash;
1✔
83
  }
84

85
  /**
86
   * Two {@code HashKeyWrapper} instances are equal when their wrapped keys are equal according to
87
   * {@link Object#equals}.
88
   */
89
  @Override
90
  public boolean equals(Object obj) {
91
    if (this == obj) {
1✔
92
      return true;
1✔
93
    }
94
    if (!(obj instanceof HashKeyWrapper)) {
1✔
95
      return false;
1✔
96
    }
97
    HashKeyWrapper other = (HashKeyWrapper) obj;
1✔
98
    return key == null ? other.key == null : key.equals(other.key);
1✔
99
  }
100

101
  @Override
102
  public String toString() {
NEW
103
    return "HashKeyWrapper{" + key + "}";
×
104
  }
105

106
  /**
107
   * Murmur3 32-bit finalizer (fmix32). Applies three xor-shift-multiply rounds that give near-perfect bit avalanche.
108
   *
109
   * @param h
110
   *          raw hash value to mix
111
   *
112
   * @return mixed hash value
113
   */
114
  static int fmix32(int h) {
115
    h ^= h >>> 16;
1✔
116
    h *= 0x85ebca6b;
1✔
117
    h ^= h >>> 13;
1✔
118
    h *= 0xc2b2ae35;
1✔
119
    h ^= h >>> 16;
1✔
120
    return h;
1✔
121
  }
122

123
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc