A JavaScript object stores named properties. It may also refer to a prototype, another object whose properties can be found through inheritance. An own property is stored directly on the object. An inherited property is found through its prototype chain. Reading a value does not tell you which of those locations holds it.
Prototype pollution occurs when untrusted input can modify a prototype used by other objects. A merge operation copies properties, and an unsafe merge can accidentally give input access to that shared structure. Ordinary inheritance itself is not a vulnerability; the failure is an unauthorized write to a shared prototype and a later use that trusts the resulting value.
Before you begin
- Recommended first
- Notes: scope, command, result, time, HTTP methods, headers, status codes, cookies, How a web app is put together, Same-origin policy, JavaScript enough to follow a few lines in a page, HTTP request smuggling
- Estimated time
- 18 min reading, 12 min activity. Setup and optional practice may take longer.
- Where you work
- Read the original fictional source, object tables, token claims and policy models in any current desktop or mobile browser. No loader, template engine, provider registration, service or network request is required.
- Without a lab
- Follow the printed data through each stated rule on paper. Record the decision, its supporting fact and a limit, then compare with the guided rubric. Model reasoning does not establish a live tool result. Reading works without JavaScript. Checking requires JavaScript and a secure context; saving also requires usable storage.
After this lesson you can:
- Define a prototype as the object another object inherits from.
- Separate the own key name from the key color on the diagram.
- Copy only allowlisted keys in the merge rule.
01Example
Use a fresh object to separate property lookup from ownership. These rows are a conceptual table, not console output.
Own properties: size = small
Prototype properties: weight = light
Lookup size → small
Lookup weight → light
weight remains on the prototype after the read.02Lookup is not ownership
In the fresh worked table, the item owns size and inherits weight. A lookup can return both values even though they have different owners. The provided original object page uses name and color as a separate evidence table. You can classify a property by its stated location without executing that page.
Constrain merge keys to the fields the application expects and reject paths that select prototypes or constructors. For permission-sensitive decisions, avoid trusting inherited defaults and use suitable own-property checks such as Object.hasOwn. These measures fit different parts of the flow; an own-property check alone cannot repair every unsafe merge.
Read a value that lookup finds, and check the ownership table before you treat it as the object's own key.Model. A teaching model drawn by hand. It shows the idea, not real tool output.
Read the full explanation below.
Own properties
size: small
Prototype
weight: light
size = smallsize resolves to small from the own-property store.
Follow the explanation
- size resolves to small from the own-property store.
- weight resolves to light on the prototype and remains there after reading.
Read every state and value
1. size resolves to small from the own-property store.
- Lookup
- size
- Selected Owner
- own
- Result Visible
- Yes
2. weight resolves to light on the prototype and remains there after reading.
- Lookup
- weight
- Selected Owner
- prototype
- Result Visible
- Yes
03Apply the allowlist
Read the evidence before comparing with the activity rubric. Do not open the HTML file in a browser for the checkpoint.
Object table. Not executed.
item own keys: name
item inherited keys: color
lookup name: lamp
lookup color: red
Use the stated key lists to classify ownership. A lookup does not move a key between those lists.
Merge rule: copy a key only when the key is on the allowlist name and title.
Do not copy a key that selects the shared prototype or the constructor.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Own and inherited</title>
</head>
<body>
<script>
var base = { color: "red" };
var item = Object.create(base);
item.name = "lamp";
</script>
</body>
</html>
- Write the own key and the inherited key from the table.
- Write the keys the merge rule is allowed to copy.
- Check: the merge copies name and title only. color stays on the list the table already gave it. You do not need to run the page.
- Stop. Do not add an assignment that selects the shared prototype.
04Optional challenge
Allow about ten minutes. Suppose an object owns enabled=false while its prototype has enabled=true. Predict the ordinary lookup and explain why checking ownership is a different question from reading the value. Rubric: the nearer own property supplies false, and an own-property check confirms where it is stored.
05Fresh cases for the checks
Use these fictional cases for the checks below. They have different inputs from the worked examples. Read them here or make notes; no installation or external service is needed.
Object case A: fresh unexecuted sourceOwned fixture. An original file made for this course. Follow the activity's instructions in your own lab.
const base = { color: "violet" };
const item = Object.create(base);
item.name = "moss";
No other keys are defined. Ordinary lookup checks an own property before the prototype.
A read does not move a property to the reading object.
Object case B: independent later statementOwned fixture. An original file made for this course. Follow the activity's instructions in your own lab.
Start from case A, then perform this source-model change:
item.color = "gold";
The statement writes only item’s own color. No prototype write occurs.
Object case C: fresh constrained mergeOwned fixture. An original file made for this course. Follow the activity's instructions in your own lab.
Allowed copied fields: name and title only.
Submitted keys: name, title, color, a path selecting the prototype, a path selecting a constructor.
Rule: copy only allowlisted fields; reject prototype/constructor paths. No merge executes.
06Explain your evidence
Optional written self-check: explain one decision using the supplied case. No tool run is required.
Check your explanation: derive own and inherited properties from the source; predict own shadowing without changing the prototype; apply the exact key allowlist; distinguish normal inheritance from an unauthorized prototype write.
07Common mistakes
If you call color own because lookup returns red, you skipped the ownership table.
If you copy every key in the merge, you left the allowlist.
If you treat the HTML file as a recorded console result, read it as a prediction. The page was not a live console.
References
- MDN: Object.hasOwn
Tests whether an object directly defines a property, independently of the value found by ordinary lookup.
- PortSwigger: Prototype pollution
Further explanation of this mechanism and its defenses. The original printed model supplies the required evidence.