Completed
Push — develop ( e80729...78b87e )
by Greg
09:14
created

individual.php (1 issue)

Severity

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
/**
3
 * webtrees: online genealogy
4
 * Copyright (C) 2017 webtrees development team
5
 * This program is free software: you can redistribute it and/or modify
6
 * it under the terms of the GNU General Public License as published by
7
 * the Free Software Foundation, either version 3 of the License, or
8
 * (at your option) any later version.
9
 * This program is distributed in the hope that it will be useful,
10
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
 * GNU General Public License for more details.
13
 * You should have received a copy of the GNU General Public License
14
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15
 */
16
namespace Fisharebest\Webtrees;
17
18
use Fisharebest\Webtrees\Controller\IndividualController;
19
use Fisharebest\Webtrees\Functions\FunctionsDate;
20
use Fisharebest\Webtrees\Functions\FunctionsDb;
21
use Fisharebest\Webtrees\Functions\FunctionsPrint;
22
23
/** @global Tree $WT_TREE */
24
global $WT_TREE;
25
26
require 'includes/session.php';
27
28
$pid    = Filter::get('pid', WT_REGEX_XREF);
29
$record = Individual::getInstance($pid, $WT_TREE);
30
if (!$record) {
31
	$record = Individual::getInstance(FunctionsDb::findRin($pid), $WT_TREE);
32
}
33
$controller = new IndividualController($record);
34
35
if ($controller->record && $controller->record->canShow()) {
36
	if (Filter::get('action') == 'ajax') {
37
		$controller->ajaxRequest();
38
39
		return;
40
	}
41
	// Generate the sidebar content *before* we display the page header,
42
	// as the clippings cart needs to have write access to the session.
43
	$sidebar_html = $controller->getSideBarContent();
44
45
	if ($controller->record->isPendingDeletion()) {
46
		if (Auth::isModerator($controller->record->getTree())) {
47
			FlashMessages::addMessage(/* I18N: %1$s is “accept”, %2$s is “reject”. These are links. */ I18N::translate(
48
				'This individual has been deleted. You should review the deletion and then %1$s or %2$s it.',
49
				'<a href="#" onclick="accept_changes(\'' . $controller->record->getXref() . '\');">' . I18N::translateContext('You should review the deletion and then accept or reject it.', 'accept') . '</a>',
50
				'<a href="#" onclick="reject_changes(\'' . $controller->record->getXref() . '\');">' . I18N::translateContext('You should review the deletion and then accept or reject it.', 'reject') . '</a>'
51
			) . ' ' . FunctionsPrint::helpLink('pending_changes'), 'warning');
52
		} elseif (Auth::isEditor($controller->record->getTree())) {
53
			FlashMessages::addMessage(I18N::translate('This individual has been deleted. The deletion will need to be reviewed by a moderator.') . ' ' . FunctionsPrint::helpLink('pending_changes'), 'warning');
54
		}
55
	} elseif ($controller->record->isPendingAddtion()) {
56
		if (Auth::isModerator($controller->record->getTree())) {
57
			FlashMessages::addMessage(/* I18N: %1$s is “accept”, %2$s is “reject”. These are links. */ I18N::translate(
58
				'This individual has been edited. You should review the changes and then %1$s or %2$s them.',
59
				'<a href="#" onclick="accept_changes(\'' . $controller->record->getXref() . '\');">' . I18N::translateContext('You should review the changes and then accept or reject them.', 'accept') . '</a>',
60
				'<a href="#" onclick="reject_changes(\'' . $controller->record->getXref() . '\');">' . I18N::translateContext('You should review the changes and then accept or reject them.', 'reject') . '</a>'
61
			) . ' ' . FunctionsPrint::helpLink('pending_changes'), 'warning');
62
		} elseif (Auth::isEditor($controller->record->getTree())) {
63
			FlashMessages::addMessage(I18N::translate('This individual has been edited. The changes need to be reviewed by a moderator.') . ' ' . FunctionsPrint::helpLink('pending_changes'), 'warning');
64
		}
65
	}
66
	$controller->pageHeader();
67
} elseif ($controller->record && $controller->record->canShowName()) {
68
	// Just show the name.
69
	$controller->pageHeader();
70
	echo '<h2>', $controller->record->getFullName(), '</h2>';
71
	echo '<p>', I18N::translate('The details of this individual are private.'), '</p>';
72
73
	return;
74
} else {
75
	FlashMessages::addMessage(I18N::translate('This individual does not exist or you do not have permission to view it.'), 'danger');
76
	http_response_code(404);
77
	$controller->pageHeader();
78
79
	return;
80
}
81
82
// If this individual is linked to a user account, show the link
83
$user_link = '';
84
if (Auth::isAdmin()) {
85
	$user = User::findByGenealogyRecord($controller->record);
86
	if ($user) {
87
		$user_link =  ' —  <a href="admin_users.php?filter=' . Filter::escapeHtml($user->getUserName()) . '">' . Filter::escapeHtml($user->getUserName()) . '</a>';
88
	};
89
}
90
91
// What is (was) the age of the individual
92
$bdate = $controller->record->getBirthDate();
93
$ddate = $controller->record->getDeathDate();
94
if ($bdate->isOK() && !$controller->record->isDead()) {
95
	// If living display age
96
	$age = ' (' . I18N::translate('age') . ' ' . FunctionsDate::getAgeAtEvent(Date::getAgeGedcom($bdate, new Date(strtoupper(date('d M Y'))))) . ')';
97
} elseif ($bdate->isOK() && $ddate->isOK()) {
98
	// If dead, show age at death
99
	$age = ' (' . I18N::translate('age') . ' ' . FunctionsDate::getAgeAtEvent(Date::getAgeGedcom($bdate, $ddate)) . ')';
100
} else {
101
	$age = '';
102
}
103
104
// Allow tabs to insert Javascript, etc.
105
// TODO: there's probably a cleaner way to do this.
106
foreach ($controller->getTabs() as $tab) {
107
	echo $tab->getPreLoadContent();
108
}
109
110
$controller->addInlineJavascript('
111
// If the URL contains a fragment, then activate the corresponding tab.
112
// Use a prefix on the fragment, to prevent scrolling to the element.
113
var target = window.location.hash.replace("tab-", "");
114
var tab = $("#individual-tabs .nav-link[href=\'" + target + "\']");
115
// If not, then activate the first tab.
116
if (tab.length === 0) {
117
	tab = $("#individual-tabs .nav-link:first");
118
}
119
tab.tab("show");
120
');
121
122
?>
123
124
<h2>
125
	<?= $controller->record->getFullName() ?><?= $user_link ?>, <?= $controller->record->getLifeSpan() ?> <?= $age ?>
126
</h2>
127
128
<div class="row">
129
	<div class="col-sm-8">
130
		<div class="row">
131
			<!-- Main image -->
132
			<div class="col-sm-3">
133
				<?= $controller->record->displayImage() ?>
134
			</div>
135
136
			<!-- Names -->
137
			<div class="col-sm-9" id="individual-names" role="tablist">
138
				<?php foreach ($controller->record->getFacts('NAME') as $n => $name_fact): ?>
139
				<?= $controller->formatNameRecord($n, $name_fact) ?>
140
				<?php endforeach ?>
141
				<?php foreach ($controller->record->getFacts('SEX') as $n => $sex_fact): ?>
142
				<?= $controller->formatSexRecord($sex_fact) ?>
143
				<?php endforeach ?>
144
145
				<?php if ($controller->record->canEdit()): ?>
146
				<div class="card">
147
					<div class="card-header" role="tab" id="name-header-add">
148
						<div class="card-title mb-0">
149
							<a href="edit_interface.php?action=addname&amp;xref=<?= $controller->record->getXref() ?>&amp;ged=<?= $controller->record->getTree()->getNameHtml() ?>">
150
								<?= I18N::translate('Add a name') ?>
151
							</a>
152
						</div>
153
					</div>
154
				</div>
155
				<?php endif ?>
156
			</div>
157
		</div>
158
159
		<div id="individual-tabs">
160
			<ul class="nav nav-tabs">
161
				<?php foreach ($controller->getTabs() as $tab): ?>
162
					<li class="nav-item">
163
						<a class="nav-link<?= $tab->isGrayedOut() ? ' text-muted' : '' ?>" data-toggle="tab" role="tab" data-href="<?= $controller->record->getHtmlUrl(), '&amp;action=ajax&amp;module=', $tab->getName() ?>" href="#<?= $tab->getName() ?>">
0 ignored issues
show
Security Cross-Site Scripting introduced by
$controller->record->getHtmlUrl() can contain request data and is used in html attribute with double-quotes context(s) leading to a potential security vulnerability.

4 paths for user data to reach this point

  1. Path: Read from $_FILES, and $file is assigned in action.php on line 112
  1. Read from $_FILES, and $file is assigned
    in action.php on line 112
  2. $gedcom is assigned
    in action.php on line 148
  3. $gedcom is passed to Tree::createRecord()
    in action.php on line 158
  4. $gedcom is passed to GedcomRecord::getInstance()
    in app/Tree.php on line 782
  5. $gedcom is passed to GedcomRecord::__construct()
    in app/GedcomRecord.php on line 202
  6. GedcomRecord::$gedcom is assigned
    in app/GedcomRecord.php on line 80
  7. Tainted property GedcomRecord::$gedcom is read
    in app/GedcomRecord.php on line 495
  8. GedcomRecord::privatizeGedcom() returns tainted data, and $newgedrec is assigned
    in app/Report/ReportParserGenerate.php on line 594
  9. ReportParserGenerate::$gedrec is assigned
    in app/Report/ReportParserGenerate.php on line 618
  10. Tainted property ReportParserGenerate::$gedrec is read
    in app/Report/ReportParserGenerate.php on line 1262
  11. Data is passed through explode()
    in vendor/app/Functions/Functions.php on line 160
  12. $thisSubrecord is assigned
    in vendor/app/Functions/Functions.php on line 161
  13. Data is passed through substr()
    in vendor/app/Functions/Functions.php on line 167
  14. ReportParserGenerate::$desc is assigned
    in app/Report/ReportParserGenerate.php on line 1262
  15. Tainted property ReportParserGenerate::$desc is read, and $value is assigned
    in app/Report/ReportParserGenerate.php on line 1308
  16. ReportParserGenerate::$vars is assigned
    in app/Report/ReportParserGenerate.php on line 1358
  17. Tainted property ReportParserGenerate::$vars is read, and $id is assigned
    in app/Report/ReportParserGenerate.php on line 827
  18. $id is passed to GedcomRecord::getInstance()
    in app/Report/ReportParserGenerate.php on line 841
  19. $xref is passed to GedcomRecord::__construct()
    in app/GedcomRecord.php on line 202
  20. GedcomRecord::$xref is assigned
    in app/GedcomRecord.php on line 79
  21. Tainted property GedcomRecord::$xref is read
    in app/GedcomRecord.php on line 280
  22. GedcomRecord::getXref() returns tainted data
    in app/GedcomRecord.php on line 351
  23. GedcomRecord::getLinkUrl() returns tainted data
    in app/GedcomRecord.php on line 330
  24. GedcomRecord::getHtmlUrl() returns tainted data
    in individual.php on line 163
  2. Path: Read from $_POST, and $newged is assigned in edit_interface.php on line 426
  1. Read from $_POST, and $newged is assigned
    in edit_interface.php on line 426
  2. $newged is assigned
    in edit_interface.php on line 446
  3. $newged is passed through substr(), and $newged is assigned
    in edit_interface.php on line 459
  4. $newged is passed to GedcomRecord::updateFact()
    in edit_interface.php on line 467
  5. $gedcom is passed through preg_replace(), and $gedcom is assigned
    in app/GedcomRecord.php on line 1192
  6. $gedcom is passed through trim(), and $gedcom is assigned
    in app/GedcomRecord.php on line 1193
  7. $new_gedcom is assigned
    in app/GedcomRecord.php on line 1230
  8. GedcomRecord::$gedcom is assigned
    in app/GedcomRecord.php on line 1249
  9. Tainted property GedcomRecord::$gedcom is read
    in app/GedcomRecord.php on line 495
  10. GedcomRecord::privatizeGedcom() returns tainted data, and $newgedrec is assigned
    in app/Report/ReportParserGenerate.php on line 594
  11. ReportParserGenerate::$gedrec is assigned
    in app/Report/ReportParserGenerate.php on line 618
  12. Tainted property ReportParserGenerate::$gedrec is read
    in app/Report/ReportParserGenerate.php on line 1262
  13. Data is passed through explode()
    in vendor/app/Functions/Functions.php on line 160
  14. $thisSubrecord is assigned
    in vendor/app/Functions/Functions.php on line 161
  15. Data is passed through substr()
    in vendor/app/Functions/Functions.php on line 167
  16. ReportParserGenerate::$desc is assigned
    in app/Report/ReportParserGenerate.php on line 1262
  17. Tainted property ReportParserGenerate::$desc is read, and $value is assigned
    in app/Report/ReportParserGenerate.php on line 1308
  18. ReportParserGenerate::$vars is assigned
    in app/Report/ReportParserGenerate.php on line 1358
  19. Tainted property ReportParserGenerate::$vars is read, and $id is assigned
    in app/Report/ReportParserGenerate.php on line 827
  20. $id is passed to GedcomRecord::getInstance()
    in app/Report/ReportParserGenerate.php on line 841
  21. $xref is passed to GedcomRecord::__construct()
    in app/GedcomRecord.php on line 202
  22. GedcomRecord::$xref is assigned
    in app/GedcomRecord.php on line 79
  23. Tainted property GedcomRecord::$xref is read
    in app/GedcomRecord.php on line 280
  24. GedcomRecord::getXref() returns tainted data
    in app/GedcomRecord.php on line 351
  25. GedcomRecord::getLinkUrl() returns tainted data
    in app/GedcomRecord.php on line 330
  26. GedcomRecord::getHtmlUrl() returns tainted data
    in individual.php on line 163
  3. Path: Read from $_POST, and $newged is assigned in edit_interface.php on line 430
  1. Read from $_POST, and $newged is assigned
    in edit_interface.php on line 430
  2. $newged is assigned
    in edit_interface.php on line 446
  3. $newged is passed through substr(), and $newged is assigned
    in edit_interface.php on line 459
  4. $newged is passed to GedcomRecord::updateFact()
    in edit_interface.php on line 467
  5. $gedcom is passed through preg_replace(), and $gedcom is assigned
    in app/GedcomRecord.php on line 1192
  6. $gedcom is passed through trim(), and $gedcom is assigned
    in app/GedcomRecord.php on line 1193
  7. $new_gedcom is assigned
    in app/GedcomRecord.php on line 1230
  8. GedcomRecord::$gedcom is assigned
    in app/GedcomRecord.php on line 1249
  9. Tainted property GedcomRecord::$gedcom is read
    in app/GedcomRecord.php on line 495
  10. GedcomRecord::privatizeGedcom() returns tainted data, and $newgedrec is assigned
    in app/Report/ReportParserGenerate.php on line 594
  11. ReportParserGenerate::$gedrec is assigned
    in app/Report/ReportParserGenerate.php on line 618
  12. Tainted property ReportParserGenerate::$gedrec is read
    in app/Report/ReportParserGenerate.php on line 1262
  13. Data is passed through explode()
    in vendor/app/Functions/Functions.php on line 160
  14. $thisSubrecord is assigned
    in vendor/app/Functions/Functions.php on line 161
  15. Data is passed through substr()
    in vendor/app/Functions/Functions.php on line 167
  16. ReportParserGenerate::$desc is assigned
    in app/Report/ReportParserGenerate.php on line 1262
  17. Tainted property ReportParserGenerate::$desc is read, and $value is assigned
    in app/Report/ReportParserGenerate.php on line 1308
  18. ReportParserGenerate::$vars is assigned
    in app/Report/ReportParserGenerate.php on line 1358
  19. Tainted property ReportParserGenerate::$vars is read, and $id is assigned
    in app/Report/ReportParserGenerate.php on line 827
  20. $id is passed to GedcomRecord::getInstance()
    in app/Report/ReportParserGenerate.php on line 841
  21. $xref is passed to GedcomRecord::__construct()
    in app/GedcomRecord.php on line 202
  22. GedcomRecord::$xref is assigned
    in app/GedcomRecord.php on line 79
  23. Tainted property GedcomRecord::$xref is read
    in app/GedcomRecord.php on line 280
  24. GedcomRecord::getXref() returns tainted data
    in app/GedcomRecord.php on line 351
  25. GedcomRecord::getLinkUrl() returns tainted data
    in app/GedcomRecord.php on line 330
  26. GedcomRecord::getHtmlUrl() returns tainted data
    in individual.php on line 163
  4. Path: Read from $_POST, and $newged is assigned in edit_interface.php on line 454
  1. Read from $_POST, and $newged is assigned
    in edit_interface.php on line 454
  2. $newged is passed through substr(), and $newged is assigned
    in edit_interface.php on line 459
  3. $newged is passed to GedcomRecord::updateFact()
    in edit_interface.php on line 467
  4. $gedcom is passed through preg_replace(), and $gedcom is assigned
    in app/GedcomRecord.php on line 1192
  5. $gedcom is passed through trim(), and $gedcom is assigned
    in app/GedcomRecord.php on line 1193
  6. $new_gedcom is assigned
    in app/GedcomRecord.php on line 1230
  7. GedcomRecord::$gedcom is assigned
    in app/GedcomRecord.php on line 1249
  8. Tainted property GedcomRecord::$gedcom is read
    in app/GedcomRecord.php on line 495
  9. GedcomRecord::privatizeGedcom() returns tainted data, and $newgedrec is assigned
    in app/Report/ReportParserGenerate.php on line 594
  10. ReportParserGenerate::$gedrec is assigned
    in app/Report/ReportParserGenerate.php on line 618
  11. Tainted property ReportParserGenerate::$gedrec is read
    in app/Report/ReportParserGenerate.php on line 1262
  12. Data is passed through explode()
    in vendor/app/Functions/Functions.php on line 160
  13. $thisSubrecord is assigned
    in vendor/app/Functions/Functions.php on line 161
  14. Data is passed through substr()
    in vendor/app/Functions/Functions.php on line 167
  15. ReportParserGenerate::$desc is assigned
    in app/Report/ReportParserGenerate.php on line 1262
  16. Tainted property ReportParserGenerate::$desc is read, and $value is assigned
    in app/Report/ReportParserGenerate.php on line 1308
  17. ReportParserGenerate::$vars is assigned
    in app/Report/ReportParserGenerate.php on line 1358
  18. Tainted property ReportParserGenerate::$vars is read, and $id is assigned
    in app/Report/ReportParserGenerate.php on line 827
  19. $id is passed to GedcomRecord::getInstance()
    in app/Report/ReportParserGenerate.php on line 841
  20. $xref is passed to GedcomRecord::__construct()
    in app/GedcomRecord.php on line 202
  21. GedcomRecord::$xref is assigned
    in app/GedcomRecord.php on line 79
  22. Tainted property GedcomRecord::$xref is read
    in app/GedcomRecord.php on line 280
  23. GedcomRecord::getXref() returns tainted data
    in app/GedcomRecord.php on line 351
  24. GedcomRecord::getLinkUrl() returns tainted data
    in app/GedcomRecord.php on line 330
  25. GedcomRecord::getHtmlUrl() returns tainted data
    in individual.php on line 163

Preventing Cross-Site-Scripting Attacks

Cross-Site-Scripting allows an attacker to inject malicious code into your website - in particular Javascript code, and have that code executed with the privileges of a visiting user. This can be used to obtain data, or perform actions on behalf of that visiting user.

In order to prevent this, make sure to escape all user-provided data:

// for HTML
$sanitized = htmlentities($tainted, ENT_QUOTES);

// for URLs
$sanitized = urlencode($tainted);

General Strategies to prevent injection

In general, it is advisable to prevent any user-data to reach this point. This can be done by white-listing certain values:

if ( ! in_array($value, array('this-is-allowed', 'and-this-too'), true)) {
    throw new \InvalidArgumentException('This input is not allowed.');
}

For numeric data, we recommend to explicitly cast the data:

$sanitized = (integer) $tainted;
Loading history...
164
							<?= $tab->getTitle() ?>
165
						</a>
166
					</li>
167
					<?php endforeach ?>
168
			</ul>
169
			<div class="tab-content">
170
				<?php	foreach ($controller->getTabs() as $tab): ?>
171
					<div id="<?= $tab->getName() ?>" class="tab-pane fade wt-ajax-load" role="tabpanel"><?php if (!$tab->canLoadAjax()): ?>
172
						<?= $tab->getTabContent() ?>
173
					<?php endif ?></div>
174
				<?php endforeach ?>
175
			</div>
176
		</div>
177
	</div>
178
	<div class="col-sm-4">
179
		<?= $sidebar_html ?>
180
	</div>
181
</div>
182