1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
//! Bounded property/variable values for manifest interpolation (issue #1481).
//!
//! Maven's `${property}` and Gradle's `version.ref`/`$name`/`${name}` substitutions each
//! resolve through a `HashMap` keyed by property name. Without a per-value bound, a manifest
//! declaring up to [`deps_core::MAX_DEPENDENCIES_PER_DOCUMENT`](crate::MAX_DEPENDENCIES_PER_DOCUMENT)
//! kept dependencies, each resolving an unbounded property value, retains
//! `kept_deps * value_len` bytes with no cap on the second factor (CWE-400). [`PropertyValue`]
//! makes an oversized value unrepresentable in the map that feeds resolution, so every
//! reference to it falls through the existing "unresolved" path instead of retaining the raw
//! text.
/// Maximum byte length of a single interpolated property/variable value.
///
/// Real version strings and version ranges are well under 100 bytes; 1 KiB leaves headroom
/// for `systemPath`-style path values while still bounding worst-case retained memory to
/// `MAX_DEPENDENCIES_PER_DOCUMENT * MAX_INTERPOLATED_VALUE_BYTES` per resolution pass.
pub const MAX_INTERPOLATED_VALUE_BYTES: usize = 1024;
/// A property/variable value that has been checked against
/// [`MAX_INTERPOLATED_VALUE_BYTES`] and is guaranteed not to exceed it.
///
/// This is the type-level gate for issue #1481: `HashMap<String, PropertyValue>` (rather than
/// `HashMap<String, String>`) as the map type that feeds Maven's `${property}` and Gradle's
/// `version.ref`/`$name`/`${name}` resolution makes an oversized retained value
/// unrepresentable, instead of relying on a runtime check at every call site.
///
/// # Examples
///
/// ```
/// use deps_core::interpolation::{MAX_INTERPOLATED_VALUE_BYTES, PropertyValue};
///
/// let value = PropertyValue::new("1.2.3".to_string()).unwrap();
/// assert_eq!(value.as_str(), "1.2.3");
///
/// let oversized = "x".repeat(MAX_INTERPOLATED_VALUE_BYTES + 1);
/// assert!(PropertyValue::new(oversized).is_err());
/// ```
;
/// Inserts `value` into `map` under `key`, dropping it instead if it exceeds
/// [`MAX_INTERPOLATED_VALUE_BYTES`] (#1481) rather than retaining an unbounded value.
///
/// Every ecosystem's own bounded-property map (`deps-maven`'s `properties`, `deps-gradle`'s
/// `version_refs` and `gradle.properties` map) shares this same insert-time gate instead of
/// each reimplementing the check-log-drop pattern independently. Logs at `debug` with the
/// value's length and `context` only — never the value's content — so a caller can tell
/// which map dropped a value without leaking a potentially large or sensitive payload into
/// tracing output. A dropped value falls through to whatever "unresolved" behavior the
/// caller's own lookup path already has for a missing key.
///
/// # Examples
///
/// ```
/// use deps_core::interpolation::{MAX_INTERPOLATED_VALUE_BYTES, insert_bounded};
/// use std::collections::HashMap;
///
/// let mut map = HashMap::new();
/// insert_bounded(&mut map, "ver".to_string(), "1.2.3".to_string(), "example");
/// assert_eq!(map.get("ver").map(|v| v.as_str()), Some("1.2.3"));
///
/// insert_bounded(
/// &mut map,
/// "big".to_string(),
/// "x".repeat(MAX_INTERPOLATED_VALUE_BYTES + 1),
/// "example",
/// );
/// assert!(!map.contains_key("big"));
/// ```
/// A property/variable value exceeded [`MAX_INTERPOLATED_VALUE_BYTES`] and was rejected by
/// [`PropertyValue::new`].
///
/// # Examples
///
/// ```
/// use deps_core::interpolation::{MAX_INTERPOLATED_VALUE_BYTES, PropertyValue};
///
/// let err = PropertyValue::new("x".repeat(MAX_INTERPOLATED_VALUE_BYTES + 1)).unwrap_err();
/// assert_eq!(err.len, MAX_INTERPOLATED_VALUE_BYTES + 1);
/// ```