EthicsComputerCourses
Join training +91 90532 10052

Scope, Permission & Rules of Engagement

Ethical Hacking · Lesson 3 of 28 Intermediate 6 min read Updated

By the end of this lesson you will be able to write the three documents that come before any real test: the scope, the authorisation and the rules of engagement. You will also build a small Python "scope guard" that refuses targets you are not allowed to touch.

Beginners often want to skip straight to scanning. Professionals know that a test is only as safe as its paperwork. A missing line in a scope document can turn good work into unauthorised access.

Three documents, three questions

Think of a wedding caterer in Lucknow. Before cooking, they agree what to serve, who approved the menu, and how the kitchen will run on the day. A test needs the same three answers.

DocumentQuestion it answersWho signs
ScopeWhat may be tested, and what may not?Client and tester
Authorisation letterWho says this test is allowed?A person with real authority over the systems
Rules of engagement (RoE)How will testing run, day to day?Client and tester

In small jobs these may be one combined document. In large ones they are separate, often attached to a contract called a statement of work (SOW). The Vulnerability Assessment vs Penetration Testing lesson explains the different kinds of engagement you might scope.

Writing the scope

A scope is a precise list of targets. Precise means addresses, host names, URLs and app versions, not vague words like "our website".

Step 1. List every in-scope target with its exact identifier.

Step 2. List exclusions just as clearly. Third-party services (payment gateways, email providers, CDNs) are usually excluded, because the client does not own them.

Step 3. List test types: network scan, web app test, authenticated testing with given accounts, and so on.

Step 4. List forbidden methods. Most scopes forbid denial of service, social engineering of staff and physical entry unless agreed separately.

Here is the scope this course uses for its lab:

Text
ENGAGEMENT: Home lab practice assessment
IN SCOPE
  192.168.56.101  Ubuntu target: Juice Shop (tcp/3000), DVWA (tcp/4280)
  192.168.56.103  Metasploitable 2
EXCLUDED
  192.168.56.1    Host computer (the hypervisor's own adapter)
  Any address outside 192.168.56.0/24
ALLOWED
  Network scanning, service enumeration, web application testing,
  password testing of lab accounts, Metasploit modules against .103 only
FORBIDDEN
  Denial of service, any traffic leaving the host-only network

After writing it, read it aloud. If a stranger could misunderstand a line, rewrite it.

The authorisation letter

The authorisation letter proves that the owner agreed. It should name the tester (person or company), the systems (by reference to the scope), the dates, and the signer's role. The signer must actually control those systems. A marketing manager cannot authorise tests on servers run by a hosting company.

Keep a signed copy with you during testing. If someone calls the police because they noticed scanning, that letter is your first answer.

Rules of engagement

Rules of engagement describe how the test runs. Good RoE cover:

  • Testing window: for example, Monday to Friday, 10:00 to 18:00 IST. Production tests are often at night to reduce business impact.
  • Source addresses: the IP addresses your traffic will come from, so the client's team can recognise it.
  • Contacts: a technical contact and an emergency contact with phone numbers, reachable during the window.
  • Stop conditions: when you must pause and call. Typical ones are a service crash, finding signs of a real attacker, or seeing highly sensitive data.
  • Critical finding rule: serious issues are reported at once, not saved for the final report.
  • Data handling: how evidence is stored, encrypted and deleted after the job.
  • Communication: how and how often you update the client.

Building a scope guard

People make typing mistakes. Typing 192.168.65.101 instead of 192.168.56.101 sends traffic somewhere you have no permission. A scope guard is a small check that every tool command goes through first. Python's ipaddress module makes this easy.

PythonRun it yourself
import ipaddress

IN_SCOPE = [ipaddress.ip_network("192.168.56.0/24")]
EXCLUDED = {ipaddress.ip_address("192.168.56.1")}

def check(target):
    try:
        ip = ipaddress.ip_address(target)
    except ValueError:
        return "REFUSE: not an IP address"
    if ip in EXCLUDED:
        return "REFUSE: excluded"
    if any(ip in net for net in IN_SCOPE):
        return "ok"
    return "REFUSE: outside scope"

for target in ["192.168.56.101", "192.168.56.1", "192.168.65.101", "10.0.0.5", "lab-target"]:
    print(f"{target:<16} {check(target)}")
Output
192.168.56.101   ok
192.168.56.1     REFUSE: excluded
192.168.65.101   REFUSE: outside scope
10.0.0.5         REFUSE: outside scope
lab-target       REFUSE: not an IP address

Notice the typo 192.168.65.101 is refused. Host names are refused too, because a name could resolve to any address. Resolve names yourself and check the resulting IP.

Testing windows need checking as well. The next example also checks the time:

PythonRun it yourself
from datetime import datetime, time

WINDOW_DAYS = {0, 1, 2, 3, 4}          # Monday=0 ... Friday=4
WINDOW_START, WINDOW_END = time(10, 0), time(18, 0)

def in_window(moment):
    return moment.weekday() in WINDOW_DAYS and WINDOW_START <= moment.time() <= WINDOW_END

samples = [
    datetime(2026, 11, 3, 11, 15),   # Tuesday morning
    datetime(2026, 11, 3, 19, 5),    # Tuesday evening
    datetime(2026, 11, 7, 11, 0),    # Saturday
]
for moment in samples:
    status = "inside window" if in_window(moment) else "OUTSIDE window: do not test"
    print(moment.strftime("%a %d %b %H:%M"), "->", status)
Output
Tue 03 Nov 11:15 -> inside window
Tue 03 Nov 19:05 -> OUTSIDE window: do not test
Sat 07 Nov 11:00 -> OUTSIDE window: do not test

Keeping an activity log

The last part of good engagement practice is an activity log: a time-stamped record of every command you ran and against what. If the client asks, "What hit our server at 14:32?", your log answers. A simple text file works:

Text
2026-11-03 10:05 IST  START  Authorisation ref LAB-001 checked
2026-11-03 10:07 IST  nmap -sn 192.168.56.0/24          (host discovery)
2026-11-03 10:12 IST  nmap -sV -p- 192.168.56.101       (full TCP scan)
2026-11-03 11:40 IST  STOP   Juice Shop container restarted, contact informed

On Linux, the script command can record a whole terminal session to a file, which saves typing.

Common mistakes

  • Vague scope words. "The website" might mean one server or fifty. Use exact identifiers.
  • Forgetting exclusions. If a payment page is hosted by a gateway, it must be listed as excluded.
  • No emergency contact. If something breaks at night, you need a person, not an email address.
  • Testing on a stale authorisation. If dates pass, get a new signed extension before continuing.

Key points

  • Scope says what, authorisation says who allowed it, rules of engagement say how.
  • Scopes use exact IPs, host names and URLs, with clear exclusions for third-party systems.
  • Rules of engagement include windows, source IPs, contacts, stop conditions and data handling.
  • A scope guard script catches typing mistakes before traffic leaves your machine.
  • Keep a time-stamped activity log of every command you run.

Exercise

Extend the scope guard so it accepts a list of targets and a list of TCP ports. Only ports 3000 and 4280 are allowed on 192.168.56.101, and any port is allowed on 192.168.56.103. Print a decision for each target and port pair.

Show solution
Python
import ipaddress

ALLOWED_PORTS = {
    ipaddress.ip_address("192.168.56.101"): {3000, 4280},
    ipaddress.ip_address("192.168.56.103"): None,   # None means any port
}

def check(target, port):
    ip = ipaddress.ip_address(target)
    if ip not in ALLOWED_PORTS:
        return "REFUSE: host not in scope"
    ports = ALLOWED_PORTS[ip]
    if ports is not None and port not in ports:
        return "REFUSE: port not in scope"
    return "ok"

for target, port in [("192.168.56.101", 3000), ("192.168.56.101", 22),
                     ("192.168.56.103", 21), ("192.168.56.104", 80)]:
    print(target, port, check(target, port))

Using a dictionary keyed by IP keeps the rules in one place, and None cleanly means "all ports allowed" for the Metasploitable target.

Quick Quiz

1. Which document answers "who says this test is allowed"?

2. Why are payment gateways usually excluded from a shop's test scope?

3. During testing, Juice Shop stops responding. What do the rules of engagement usually say?

4. Why does the scope guard refuse host names like lab-target?

Learn Ethical Hacking with a mentor

Free lessons get you started. Our Ethical Hacking & Cyber Security training adds real projects and code reviews from working developers.

  • Project-based learning
  • Mentor code reviews
  • Free counselling call